ilk
This commit is contained in:
74
svc/queries/account.go
Normal file
74
svc/queries/account.go
Normal file
@@ -0,0 +1,74 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/internal/authz"
|
||||
"bssapp-backend/models"
|
||||
)
|
||||
|
||||
func GetAccounts(ctx context.Context) ([]models.Account, error) {
|
||||
|
||||
piyasaFilter := authz.BuildMSSQLPiyasaFilter(ctx, "f2.CustomerAtt01")
|
||||
|
||||
if strings.TrimSpace(piyasaFilter) == "" {
|
||||
piyasaFilter = "1=1"
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
x.AccountCode,
|
||||
MAX(x.AccountName) AS AccountName
|
||||
FROM (
|
||||
SELECT
|
||||
LEFT(b.CurrAccCode, 8) AS AccountCode,
|
||||
COALESCE(d.CurrAccDescription, '') AS AccountName
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON d.CurrAccCode = b.CurrAccCode
|
||||
JOIN CustomerAttributesFilter f2
|
||||
ON f2.CurrAccCode = b.CurrAccCode
|
||||
WHERE %s
|
||||
) x
|
||||
GROUP BY x.AccountCode
|
||||
ORDER BY x.AccountCode
|
||||
`, piyasaFilter)
|
||||
|
||||
log.Println("🔎 ACCOUNT PIYASA FILTER =", piyasaFilter)
|
||||
log.Println("🔎 ACCOUNT QUERY =", query)
|
||||
|
||||
rows, err := db.MssqlDB.Query(query)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MSSQL query error: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var accounts []models.Account
|
||||
|
||||
for rows.Next() {
|
||||
|
||||
var acc models.Account
|
||||
|
||||
if err := rows.Scan(
|
||||
&acc.AccountCode,
|
||||
&acc.AccountName,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(acc.AccountCode) >= 4 {
|
||||
acc.DisplayCode =
|
||||
strings.TrimSpace(acc.AccountCode[:3] + " " + acc.AccountCode[3:])
|
||||
} else {
|
||||
acc.DisplayCode = acc.AccountCode
|
||||
}
|
||||
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
|
||||
return accounts, rows.Err()
|
||||
}
|
||||
64
svc/queries/currency_cache.go
Normal file
64
svc/queries/currency_cache.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
/* ===============================
|
||||
CACHE STRUCT
|
||||
================================ */
|
||||
|
||||
type currencyCacheItem struct {
|
||||
data *models.TodayCurrencyV3
|
||||
expiresAt time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
currencyCache = make(map[string]currencyCacheItem)
|
||||
cacheMutex sync.RWMutex
|
||||
cacheTTL = 5 * time.Minute
|
||||
)
|
||||
|
||||
/* ===============================
|
||||
MAIN CACHE FUNC
|
||||
================================ */
|
||||
|
||||
func GetCachedCurrencyV3(db *sql.DB, code string) (*models.TodayCurrencyV3, error) {
|
||||
|
||||
now := time.Now()
|
||||
|
||||
/* ---------- READ CACHE ---------- */
|
||||
cacheMutex.RLock()
|
||||
|
||||
item, ok := currencyCache[code]
|
||||
|
||||
if ok && now.Before(item.expiresAt) {
|
||||
cacheMutex.RUnlock()
|
||||
return item.data, nil
|
||||
}
|
||||
|
||||
cacheMutex.RUnlock()
|
||||
|
||||
/* ---------- FETCH DB ---------- */
|
||||
|
||||
data, err := GetTodayCurrencyV3(db, code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
/* ---------- WRITE CACHE ---------- */
|
||||
|
||||
cacheMutex.Lock()
|
||||
|
||||
currencyCache[code] = currencyCacheItem{
|
||||
data: data,
|
||||
expiresAt: now.Add(cacheTTL),
|
||||
}
|
||||
|
||||
cacheMutex.Unlock()
|
||||
|
||||
return data, nil
|
||||
}
|
||||
114
svc/queries/customerlist.go
Normal file
114
svc/queries/customerlist.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/internal/authz"
|
||||
"bssapp-backend/models"
|
||||
)
|
||||
|
||||
func GetCustomerList(ctx context.Context) ([]models.CustomerList, error) {
|
||||
|
||||
piyasaFilter := authz.BuildMSSQLPiyasaFilter(
|
||||
ctx,
|
||||
"f.CustomerAtt01",
|
||||
)
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
c.CurrAccTypeCode,
|
||||
c.CurrAccCode,
|
||||
ISNULL(d.CurrAccDescription, ''),
|
||||
|
||||
dbo.HG_Temizlik(
|
||||
ISNULL((
|
||||
SELECT AttributeDescription
|
||||
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
|
||||
WHERE CurrAccTypeCode = 3
|
||||
AND AttributeTypeCode = 8
|
||||
AND AttributeCode = f.CustomerAtt08
|
||||
AND LangCode = 'TR'
|
||||
), SPACE(0))
|
||||
),
|
||||
|
||||
dbo.HG_Temizlik(
|
||||
ISNULL((
|
||||
SELECT AttributeDescription
|
||||
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
|
||||
WHERE CurrAccTypeCode = 3
|
||||
AND AttributeTypeCode = 1
|
||||
AND AttributeCode = f.CustomerAtt01
|
||||
AND LangCode = 'TR'
|
||||
), SPACE(0))
|
||||
),
|
||||
|
||||
dbo.HG_Temizlik(
|
||||
ISNULL((
|
||||
SELECT AttributeDescription
|
||||
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
|
||||
WHERE CurrAccTypeCode = 3
|
||||
AND AttributeTypeCode = 2
|
||||
AND AttributeCode = f.CustomerAtt02
|
||||
AND LangCode = 'TR'
|
||||
), SPACE(0))
|
||||
),
|
||||
|
||||
dbo.HG_Temizlik(
|
||||
ISNULL((
|
||||
SELECT AttributeDescription
|
||||
FROM cdCurrAccAttributeDesc WITH(NOLOCK)
|
||||
WHERE CurrAccTypeCode = 3
|
||||
AND AttributeTypeCode = 5
|
||||
AND AttributeCode = f.CustomerAtt05
|
||||
AND LangCode = 'TR'
|
||||
), SPACE(0))
|
||||
),
|
||||
|
||||
ISNULL(c.CurrencyCode, '')
|
||||
|
||||
FROM cdCurrAcc c
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON c.CurrAccCode = d.CurrAccCode
|
||||
LEFT JOIN CustomerAttributesFilter f
|
||||
ON c.CurrAccCode = f.CurrAccCode
|
||||
|
||||
WHERE
|
||||
c.CompanyCode = 1
|
||||
AND c.CurrAccTypeCode = 3
|
||||
AND c.IsBlocked = 0
|
||||
AND %s
|
||||
|
||||
ORDER BY d.CurrAccDescription
|
||||
`, piyasaFilter)
|
||||
|
||||
rows, err := db.MssqlDB.Query(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var list []models.CustomerList
|
||||
|
||||
for rows.Next() {
|
||||
var c models.CustomerList
|
||||
|
||||
if err := rows.Scan(
|
||||
&c.CurrAccTypeCode,
|
||||
&c.Cari_Kod,
|
||||
&c.Cari_Ad,
|
||||
&c.Musteri_Ana_Grubu,
|
||||
&c.Piyasa,
|
||||
&c.Musteri_Temsilcisi,
|
||||
&c.Ulke,
|
||||
&c.Doviz_cinsi,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
list = append(list, c)
|
||||
}
|
||||
|
||||
return list, rows.Err()
|
||||
}
|
||||
52
svc/queries/get_order_list_excel.go
Normal file
52
svc/queries/get_order_list_excel.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func GetOrderListExcel(
|
||||
db *sql.DB,
|
||||
search string,
|
||||
currAcc string,
|
||||
orderDate string,
|
||||
) (*sql.Rows, error) {
|
||||
|
||||
q := OrderListBaseQuery + " AND 1=1 "
|
||||
args := []interface{}{}
|
||||
|
||||
// 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)
|
||||
)
|
||||
`
|
||||
args = append(args, "%"+search+"%")
|
||||
}
|
||||
|
||||
// CURRACC
|
||||
if currAcc != "" {
|
||||
q += fmt.Sprintf(" AND h.CurrAccCode = @p%d ", len(args)+1)
|
||||
args = append(args, currAcc)
|
||||
}
|
||||
|
||||
// DATE
|
||||
if orderDate != "" {
|
||||
q += fmt.Sprintf(
|
||||
" AND CONVERT(varchar, h.OrderDate, 23) = @p%d ",
|
||||
len(args)+1,
|
||||
)
|
||||
args = append(args, orderDate)
|
||||
}
|
||||
|
||||
// ORDER BY SONDA
|
||||
q += " ORDER BY h.CreatedDate DESC "
|
||||
|
||||
return db.Query(q, args...)
|
||||
}
|
||||
174
svc/queries/helpers.go
Normal file
174
svc/queries/helpers.go
Normal file
@@ -0,0 +1,174 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// 🔥 UNIVERSAL DATETIME PARSER
|
||||
// ============================================================
|
||||
func parseDateTime(str string) (time.Time, error) {
|
||||
layouts := []string{
|
||||
"2006-01-02 15:04:05", // "2025-11-21 12:37:21"
|
||||
time.RFC3339, // "2025-11-21T12:37:21Z"
|
||||
"2006-01-02", // "2025-11-21"
|
||||
}
|
||||
for _, layout := range layouts {
|
||||
if t, err := time.Parse(layout, str); err == nil {
|
||||
return t, nil
|
||||
}
|
||||
}
|
||||
return time.Time{}, fmt.Errorf("datetime parse edilemedi: %s", str)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 DATE (YYYY-MM-DD) → sql.NullTime
|
||||
// ============================================================
|
||||
func nullableDateString(ns models.NullString) sql.NullTime {
|
||||
if ns.Valid && strings.TrimSpace(ns.String) != "" {
|
||||
t, err := time.Parse("2006-01-02", ns.String)
|
||||
if err == nil {
|
||||
return sql.NullTime{Valid: true, Time: t}
|
||||
}
|
||||
}
|
||||
return sql.NullTime{}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 TIME (HH:mm:ss) → sql.NullString
|
||||
// ============================================================
|
||||
func nullableTimeString(ns models.NullString) sql.NullString {
|
||||
if ns.Valid && strings.TrimSpace(ns.String) != "" {
|
||||
return sql.NullString{String: ns.String, Valid: true}
|
||||
}
|
||||
return sql.NullString{}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 DATETIME (CustomTime → sql.NullTime)
|
||||
// ============================================================
|
||||
func nullableDateTime(ct models.CustomTime, fallback time.Time) sql.NullTime {
|
||||
if ct.Valid && !ct.Time.IsZero() {
|
||||
return sql.NullTime{Valid: true, Time: ct.Time}
|
||||
}
|
||||
return sql.NullTime{Valid: true, Time: fallback}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 DATETIME (NullTime → sql.NullTime)
|
||||
// ============================================================
|
||||
func nullableTime(nt models.NullTime, fallback time.Time) sql.NullTime {
|
||||
if nt.Valid && !nt.Time.IsZero() {
|
||||
return sql.NullTime{Valid: true, Time: nt.Time}
|
||||
}
|
||||
return sql.NullTime{Valid: true, Time: fallback}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 DATETIME (NullString → sql.NullTime)
|
||||
// ============================================================
|
||||
func nullableDateTimeString(ns models.NullString, fallback time.Time) sql.NullTime {
|
||||
if !ns.Valid || strings.TrimSpace(ns.String) == "" {
|
||||
return sql.NullTime{Time: fallback, Valid: true}
|
||||
}
|
||||
t, err := parseDateTime(ns.String)
|
||||
if err != nil {
|
||||
return sql.NullTime{Time: fallback, Valid: true}
|
||||
}
|
||||
return sql.NullTime{Time: t, Valid: true}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 STRING → sql.NullString
|
||||
// ============================================================
|
||||
func nullableString(val models.NullString, fallback string) sql.NullString {
|
||||
if val.Valid && strings.TrimSpace(val.String) != "" {
|
||||
return sql.NullString{String: val.String, Valid: true}
|
||||
}
|
||||
return sql.NullString{String: fallback, Valid: true}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 GUID (UNIQUEIDENTIFIER) → interface{}
|
||||
// ============================================================
|
||||
func nullableUUID(v interface{}) interface{} {
|
||||
switch x := v.(type) {
|
||||
|
||||
// 1️⃣ models.NullUUID → direkt geri dön
|
||||
case models.NullUUID:
|
||||
if !x.Valid {
|
||||
return nil
|
||||
}
|
||||
return x.UUID
|
||||
|
||||
// 2️⃣ models.NullString → GUID parse et
|
||||
case models.NullString:
|
||||
if !x.Valid || strings.TrimSpace(x.String) == "" {
|
||||
return nil
|
||||
}
|
||||
id, err := uuid.Parse(x.String)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
|
||||
// 3️⃣ string → GUID parse et
|
||||
case string:
|
||||
id, err := uuid.Parse(x)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 NUMERIC → sql.NullX
|
||||
// ============================================================
|
||||
func nullableFloat64(val models.NullFloat64, fallback float64) sql.NullFloat64 {
|
||||
if val.Valid {
|
||||
return sql.NullFloat64{Float64: val.Float64, Valid: true}
|
||||
}
|
||||
return sql.NullFloat64{Float64: fallback, Valid: true}
|
||||
}
|
||||
|
||||
func nullableInt16(val models.NullInt16, fallback int16) sql.NullInt16 {
|
||||
if val.Valid {
|
||||
return sql.NullInt16{Int16: val.Int16, Valid: true}
|
||||
}
|
||||
return sql.NullInt16{Int16: fallback, Valid: true}
|
||||
}
|
||||
|
||||
func nullableInt32(val models.NullInt32, fallback int32) sql.NullInt32 {
|
||||
if val.Valid {
|
||||
return sql.NullInt32{Int32: val.Int32, Valid: true}
|
||||
}
|
||||
return sql.NullInt32{Int32: fallback, Valid: true}
|
||||
}
|
||||
|
||||
func nullableInt32ToInt16(val models.NullInt32, fallback int16) sql.NullInt16 {
|
||||
if val.Valid {
|
||||
return sql.NullInt16{Int16: int16(val.Int32), Valid: true}
|
||||
}
|
||||
return sql.NullInt16{Int16: fallback, Valid: true}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 📌 BOOL → sql.NullBool
|
||||
// ============================================================
|
||||
func nullableBool(val models.NullBool, fallback bool) sql.NullBool {
|
||||
if val.Valid {
|
||||
return sql.NullBool{Bool: val.Bool, Valid: true}
|
||||
}
|
||||
return sql.NullBool{Bool: fallback, Valid: true}
|
||||
}
|
||||
|
||||
var logger = log.Default()
|
||||
93
svc/queries/inventoryproduct.go
Normal file
93
svc/queries/inventoryproduct.go
Normal file
@@ -0,0 +1,93 @@
|
||||
package queries
|
||||
|
||||
// 🔹 Ürüne göre stok detay sorgusu
|
||||
const GetInventoryProduct = `
|
||||
-- 🔹 Ürüne göre stok detay sorgusu (Nebim V3 uyumlu, parametre güvenli)
|
||||
SELECT
|
||||
bsItemTypeDesc.ItemTypeDescription AS InventoryType,
|
||||
inv.WarehouseCode AS Depo_Kodu,
|
||||
wh.WarehouseDescription AS Depo_Adi,
|
||||
inv.ItemCode AS Urun_Kodu,
|
||||
ISNULL(descItem.ItemDescription, '') AS Madde_Aciklamasi,
|
||||
inv.ColorCode AS Renk_Kodu,
|
||||
ISNULL(descColor.ColorDescription, '') AS Renk_Aciklamasi,
|
||||
inv.ItemDim1Code AS Beden,
|
||||
attr.ProductAtt01 AS Urun_Grubu,
|
||||
attr.ProductAtt02 AS Urun_Alt_Grubu,
|
||||
attr.ProductAtt41 AS Kisa_Karisim,
|
||||
attr.ProductAtt42 AS SERI,
|
||||
attr.ProductAtt43 AS FASON_ISCLIK,
|
||||
attr.ProductAtt45 AS ASKILI_YAN,
|
||||
inv.ItemDim2Code AS YAKA,
|
||||
attr.ProductAtt44 AS GARSON_YETISKIN,
|
||||
attr.ProductAtt10 AS MarkaKodu,
|
||||
ROUND(
|
||||
SUM(inv.InventoryQty1)
|
||||
- (SUM(inv.ReserveQty1) + SUM(inv.DispOrderQty1) + SUM(inv.PickingQty1)),
|
||||
cdUnitOfMeasure.RoundDigit
|
||||
) AS Kullanilabilir_Envanter
|
||||
FROM cdItem WITH (NOLOCK)
|
||||
JOIN cdUnitOfMeasure WITH (NOLOCK)
|
||||
ON cdItem.UnitOfMeasureCode1 = cdUnitOfMeasure.UnitOfMeasureCode
|
||||
JOIN (
|
||||
SELECT
|
||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
SUM(CASE WHEN SourceTable = 'PickingStates' THEN Qty1 ELSE 0 END) AS PickingQty1,
|
||||
SUM(CASE WHEN SourceTable = 'ReserveStates' THEN Qty1 ELSE 0 END) AS ReserveQty1,
|
||||
SUM(CASE WHEN SourceTable = 'DispOrderStates' THEN Qty1 ELSE 0 END) AS DispOrderQty1,
|
||||
SUM(CASE WHEN SourceTable = 'trStock' THEN (In_Qty1 - Out_Qty1) ELSE 0 END) AS InventoryQty1
|
||||
FROM (
|
||||
SELECT 'PickingStates' AS SourceTable, CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, Qty1, 0 AS In_Qty1, 0 AS Out_Qty1
|
||||
FROM PickingStates WITH (NOLOCK)
|
||||
UNION ALL
|
||||
SELECT 'ReserveStates', CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, Qty1, 0, 0
|
||||
FROM ReserveStates WITH (NOLOCK)
|
||||
UNION ALL
|
||||
SELECT 'DispOrderStates', CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, Qty1, 0, 0
|
||||
FROM DispOrderStates WITH (NOLOCK)
|
||||
UNION ALL
|
||||
SELECT 'trStock', CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code, 0, SUM(In_Qty1), SUM(Out_Qty1)
|
||||
FROM trStock WITH (NOLOCK)
|
||||
GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||
) AS src
|
||||
GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||
) AS inv
|
||||
ON cdItem.ItemTypeCode = inv.ItemTypeCode
|
||||
AND cdItem.ItemCode = inv.ItemCode
|
||||
LEFT JOIN ProductAttributesFilter AS attr WITH (NOLOCK)
|
||||
ON attr.ItemCode = inv.ItemCode
|
||||
LEFT JOIN bsItemTypeDesc WITH (NOLOCK)
|
||||
ON bsItemTypeDesc.ItemTypeCode = inv.ItemTypeCode
|
||||
AND bsItemTypeDesc.LangCode = 'TR'
|
||||
LEFT JOIN cdWarehouseDesc AS wh WITH (NOLOCK)
|
||||
ON wh.WarehouseCode = inv.WarehouseCode
|
||||
LEFT JOIN cdItemDesc AS descItem WITH (NOLOCK)
|
||||
ON descItem.ItemTypeCode = inv.ItemTypeCode
|
||||
AND descItem.ItemCode = inv.ItemCode
|
||||
AND descItem.LangCode = 'TR'
|
||||
LEFT JOIN cdColorDesc AS descColor WITH (NOLOCK)
|
||||
ON descColor.ColorCode = inv.ColorCode
|
||||
AND descColor.LangCode = 'TR'
|
||||
WHERE
|
||||
inv.ItemTypeCode IN (1)
|
||||
AND inv.WarehouseCode IN ('1-0-12','1-0-21','1-0-10','1-0-2','1-1-3','1-2-4','1-2-5','')
|
||||
AND inv.InventoryQty1 >= 0
|
||||
AND cdItem.IsBlocked = 0
|
||||
AND inv.ItemCode = @p1 -- ✅ doğrudan parametre, DECLARE yok
|
||||
GROUP BY
|
||||
inv.CompanyCode, inv.OfficeCode, inv.StoreTypeCode, inv.StoreCode,
|
||||
inv.WarehouseCode, inv.ItemTypeCode, inv.ItemCode, inv.ColorCode,
|
||||
inv.ItemDim1Code, inv.ItemDim2Code, inv.ItemDim3Code,
|
||||
cdUnitOfMeasure.RoundDigit, attr.ProductAtt01, attr.ProductAtt02,
|
||||
attr.ProductAtt41, attr.ProductAtt42, attr.ProductAtt43, attr.ProductAtt44,
|
||||
attr.ProductAtt45, attr.ProductAtt10, bsItemTypeDesc.ItemTypeDescription,
|
||||
wh.WarehouseDescription, descItem.ItemDescription, descColor.ColorDescription;
|
||||
|
||||
`
|
||||
280
svc/queries/order_get.go
Normal file
280
svc/queries/order_get.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetOrderByID — Sipariş başlığı (header) ve satırlarını (lines) getirir.
|
||||
func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, error) {
|
||||
conn := db.GetDB()
|
||||
|
||||
logger.Printf("🧾 [GetOrderByID] begin • id=%s", orderID)
|
||||
|
||||
// =====================================================
|
||||
// HEADER (Cari adı join'li)
|
||||
// =====================================================
|
||||
var header models.OrderHeader
|
||||
qHeader := `
|
||||
SELECT
|
||||
CAST(h.OrderHeaderID AS varchar(36)) AS OrderHeaderID,
|
||||
h.OrderTypeCode,
|
||||
h.ProcessCode,
|
||||
h.OrderNumber,
|
||||
h.IsCancelOrder,
|
||||
h.OrderDate,
|
||||
h.OrderTime,
|
||||
h.DocumentNumber,
|
||||
h.PaymentTerm,
|
||||
h.AverageDueDate,
|
||||
h.Description,
|
||||
h.InternalDescription,
|
||||
h.CurrAccTypeCode,
|
||||
h.CurrAccCode,
|
||||
d.CurrAccDescription,
|
||||
h.SubCurrAccID,
|
||||
h.ContactID,
|
||||
h.ShipmentMethodCode,
|
||||
h.ShippingPostalAddressID,
|
||||
h.BillingPostalAddressID,
|
||||
h.GuarantorContactID,
|
||||
h.GuarantorContactID2,
|
||||
h.RoundsmanCode,
|
||||
h.DeliveryCompanyCode,
|
||||
h.TaxTypeCode,
|
||||
h.WithHoldingTaxTypeCode,
|
||||
h.DOVCode,
|
||||
h.TaxExemptionCode,
|
||||
h.CompanyCode,
|
||||
h.OfficeCode,
|
||||
h.StoreTypeCode,
|
||||
h.StoreCode,
|
||||
h.POSTerminalID,
|
||||
h.WarehouseCode,
|
||||
h.ToWarehouseCode,
|
||||
h.OrdererCompanyCode,
|
||||
h.OrdererOfficeCode,
|
||||
h.OrdererStoreCode,
|
||||
h.GLTypeCode,
|
||||
h.DocCurrencyCode,
|
||||
h.LocalCurrencyCode,
|
||||
h.ExchangeRate,
|
||||
h.TDisRate1,
|
||||
h.TDisRate2,
|
||||
h.TDisRate3,
|
||||
h.TDisRate4,
|
||||
h.TDisRate5,
|
||||
h.DiscountReasonCode,
|
||||
h.SurplusOrderQtyToleranceRate,
|
||||
h.ImportFileNumber,
|
||||
h.ExportFileNumber,
|
||||
h.IncotermCode1,
|
||||
h.IncotermCode2,
|
||||
h.LettersOfCreditNumber,
|
||||
h.PaymentMethodCode,
|
||||
h.IsInclutedVat,
|
||||
h.IsCreditSale,
|
||||
h.IsCreditableConfirmed,
|
||||
h.CreditableConfirmedUser,
|
||||
h.CreditableConfirmedDate,
|
||||
h.IsSalesViaInternet,
|
||||
h.IsSuspended,
|
||||
h.IsCompleted,
|
||||
h.IsPrinted,
|
||||
h.IsLocked,
|
||||
h.UserLocked,
|
||||
h.IsClosed,
|
||||
h.ApplicationCode,
|
||||
h.ApplicationID,
|
||||
h.CreatedUserName,
|
||||
h.CreatedDate,
|
||||
h.LastUpdatedUserName,
|
||||
h.LastUpdatedDate,
|
||||
h.IsProposalBased
|
||||
FROM BAGGI_V3.dbo.trOrderHeader AS h
|
||||
LEFT JOIN BAGGI_V3.dbo.cdCurrAccDesc AS d
|
||||
ON h.CurrAccCode = d.CurrAccCode
|
||||
WHERE h.OrderHeaderID = @p1;
|
||||
`
|
||||
|
||||
err := conn.QueryRow(qHeader, orderID).Scan(
|
||||
&header.OrderHeaderID,
|
||||
&header.OrderTypeCode,
|
||||
&header.ProcessCode,
|
||||
&header.OrderNumber,
|
||||
&header.IsCancelOrder,
|
||||
&header.OrderDate,
|
||||
&header.OrderTime,
|
||||
&header.DocumentNumber,
|
||||
&header.PaymentTerm,
|
||||
&header.AverageDueDate,
|
||||
&header.Description,
|
||||
&header.InternalDescription,
|
||||
&header.CurrAccTypeCode,
|
||||
&header.CurrAccCode,
|
||||
&header.CurrAccDescription,
|
||||
&header.SubCurrAccID,
|
||||
&header.ContactID,
|
||||
&header.ShipmentMethodCode,
|
||||
&header.ShippingPostalAddressID,
|
||||
&header.BillingPostalAddressID,
|
||||
&header.GuarantorContactID,
|
||||
&header.GuarantorContactID2,
|
||||
&header.RoundsmanCode,
|
||||
&header.DeliveryCompanyCode,
|
||||
&header.TaxTypeCode,
|
||||
&header.WithHoldingTaxTypeCode,
|
||||
&header.DOVCode,
|
||||
&header.TaxExemptionCode,
|
||||
&header.CompanyCode,
|
||||
&header.OfficeCode,
|
||||
&header.StoreTypeCode,
|
||||
&header.StoreCode,
|
||||
&header.POSTerminalID,
|
||||
&header.WarehouseCode,
|
||||
&header.ToWarehouseCode,
|
||||
&header.OrdererCompanyCode,
|
||||
&header.OrdererOfficeCode,
|
||||
&header.OrdererStoreCode,
|
||||
&header.GLTypeCode,
|
||||
&header.DocCurrencyCode,
|
||||
&header.LocalCurrencyCode,
|
||||
&header.ExchangeRate,
|
||||
&header.TDisRate1,
|
||||
&header.TDisRate2,
|
||||
&header.TDisRate3,
|
||||
&header.TDisRate4,
|
||||
&header.TDisRate5,
|
||||
&header.DiscountReasonCode,
|
||||
&header.SurplusOrderQtyToleranceRate,
|
||||
&header.ImportFileNumber,
|
||||
&header.ExportFileNumber,
|
||||
&header.IncotermCode1,
|
||||
&header.IncotermCode2,
|
||||
&header.LettersOfCreditNumber,
|
||||
&header.PaymentMethodCode,
|
||||
&header.IsInclutedVat,
|
||||
&header.IsCreditSale,
|
||||
&header.IsCreditableConfirmed,
|
||||
&header.CreditableConfirmedUser,
|
||||
&header.CreditableConfirmedDate,
|
||||
&header.IsSalesViaInternet,
|
||||
&header.IsSuspended,
|
||||
&header.IsCompleted,
|
||||
&header.IsPrinted,
|
||||
&header.IsLocked,
|
||||
&header.UserLocked,
|
||||
&header.IsClosed,
|
||||
&header.ApplicationCode,
|
||||
&header.ApplicationID,
|
||||
&header.CreatedUserName,
|
||||
&header.CreatedDate,
|
||||
&header.LastUpdatedUserName,
|
||||
&header.LastUpdatedDate,
|
||||
&header.IsProposalBased,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
logger.Printf("⚠️ [GetOrderByID] sipariş bulunamadı: %s", orderID)
|
||||
return nil, nil, sql.ErrNoRows
|
||||
}
|
||||
logger.Printf("❌ [GetOrderByID] header sorgu hatası: %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;
|
||||
`
|
||||
|
||||
rows, err := conn.Query(qLines, orderID)
|
||||
if err != nil {
|
||||
logger.Printf("❌ [GetOrderByID] line sorgu hatası: %v", err)
|
||||
return &header, nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
lines := make([]models.OrderDetail, 0, 32)
|
||||
for rows.Next() {
|
||||
var ln models.OrderDetail
|
||||
if err := rows.Scan(
|
||||
&ln.OrderLineID,
|
||||
&ln.SortOrder,
|
||||
&ln.ItemTypeCode,
|
||||
&ln.ItemCode,
|
||||
&ln.ColorCode,
|
||||
&ln.ItemDim1Code,
|
||||
&ln.ItemDim2Code,
|
||||
&ln.ItemDim3Code,
|
||||
&ln.Qty1,
|
||||
&ln.Qty2,
|
||||
&ln.Price,
|
||||
&ln.VatRate,
|
||||
&ln.PCTRate,
|
||||
&ln.DocCurrencyCode,
|
||||
&ln.DeliveryDate,
|
||||
&ln.PlannedDateOfLading,
|
||||
&ln.LineDescription,
|
||||
&ln.IsClosed,
|
||||
&ln.CreatedUserName,
|
||||
&ln.CreatedDate,
|
||||
&ln.LastUpdatedUserName,
|
||||
&ln.LastUpdatedDate,
|
||||
&ln.UrunIlkGrubu,
|
||||
&ln.UrunAnaGrubu,
|
||||
&ln.UrunAltGrubu,
|
||||
&ln.Fit1,
|
||||
&ln.Fit2,
|
||||
); err != nil {
|
||||
return &header, nil, fmt.Errorf("line scan hatası: %w", err)
|
||||
}
|
||||
lines = append(lines, ln)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return &header, nil, fmt.Errorf("line rows hatası: %w", err)
|
||||
}
|
||||
|
||||
logger.Printf("📦 [GetOrderByID] lines loaded • count=%d", len(lines))
|
||||
return &header, lines, nil
|
||||
}
|
||||
161
svc/queries/order_pdf.go
Normal file
161
svc/queries/order_pdf.go
Normal file
@@ -0,0 +1,161 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
)
|
||||
|
||||
/*
|
||||
============================================================
|
||||
|
||||
HEADER GETIRME — OrderHeader struct’ına uygun
|
||||
============================================================
|
||||
*/
|
||||
type OrderHeaderDB struct {
|
||||
OrderHeaderID string
|
||||
OrderNumber string
|
||||
CurrAccCode string
|
||||
CurrAccName string
|
||||
DocCurrency string
|
||||
OrderDate sql.NullTime
|
||||
Description sql.NullString
|
||||
InternalDesc sql.NullString
|
||||
OfficeCode sql.NullString
|
||||
CreatedUser sql.NullString
|
||||
}
|
||||
|
||||
func GetOrderHeaderDB(ctx context.Context, db *sql.DB, id string) (*OrderHeaderDB, error) {
|
||||
q := `
|
||||
SELECT
|
||||
CAST(h.OrderHeaderID AS varchar(36)),
|
||||
h.OrderNumber,
|
||||
h.CurrAccCode,
|
||||
d.CurrAccDescription,
|
||||
h.DocCurrencyCode,
|
||||
h.OrderDate,
|
||||
h.Description,
|
||||
h.InternalDescription,
|
||||
h.OfficeCode,
|
||||
h.CreatedUserName
|
||||
FROM BAGGI_V3.dbo.trOrderHeader AS h
|
||||
LEFT JOIN BAGGI_V3.dbo.cdCurrAccDesc AS d
|
||||
ON h.CurrAccCode = d.CurrAccCode
|
||||
WHERE h.OrderHeaderID = @p1
|
||||
`
|
||||
row := db.QueryRowContext(ctx, q, id)
|
||||
|
||||
var h OrderHeaderDB
|
||||
err := row.Scan(
|
||||
&h.OrderHeaderID,
|
||||
&h.OrderNumber,
|
||||
&h.CurrAccCode,
|
||||
&h.CurrAccName,
|
||||
&h.DocCurrency,
|
||||
&h.OrderDate,
|
||||
&h.Description,
|
||||
&h.InternalDesc,
|
||||
&h.OfficeCode,
|
||||
&h.CreatedUser,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &h, nil
|
||||
}
|
||||
|
||||
/*
|
||||
============================================================
|
||||
|
||||
SATIRLARI GETIRME — OrderLineRaw struct’ına uygun
|
||||
============================================================
|
||||
*/
|
||||
type OrderLineRawDB struct {
|
||||
OrderLineID sql.NullString
|
||||
ItemCode string
|
||||
ColorCode string
|
||||
ItemDim1Code sql.NullString
|
||||
ItemDim2Code sql.NullString
|
||||
Qty1 sql.NullFloat64
|
||||
Price sql.NullFloat64
|
||||
DocCurrencyCode sql.NullString
|
||||
DeliveryDate sql.NullTime
|
||||
LineDescription sql.NullString
|
||||
UrunAnaGrubu sql.NullString
|
||||
UrunAltGrubu sql.NullString
|
||||
IsClosed sql.NullBool
|
||||
WithHoldingTaxType sql.NullString
|
||||
DOVCode sql.NullString
|
||||
PlannedDateOfLading sql.NullTime
|
||||
CostCenterCode sql.NullString
|
||||
VatCode sql.NullString
|
||||
VatRate sql.NullFloat64
|
||||
}
|
||||
|
||||
func GetOrderLinesDB(ctx context.Context, db *sql.DB, id string) ([]OrderLineRawDB, error) {
|
||||
|
||||
q := `
|
||||
SELECT
|
||||
CAST(L.OrderLineID AS varchar(36)),
|
||||
L.ItemCode,
|
||||
L.ColorCode,
|
||||
L.ItemDim1Code,
|
||||
L.ItemDim2Code,
|
||||
L.Qty1,
|
||||
L.Price,
|
||||
L.DocCurrencyCode,
|
||||
L.DeliveryDate,
|
||||
L.LineDescription,
|
||||
P.ProductAtt01Desc,
|
||||
P.ProductAtt02Desc,
|
||||
L.IsClosed,
|
||||
L.WithHoldingTaxTypeCode,
|
||||
L.DOVCode,
|
||||
L.PlannedDateOfLading,
|
||||
L.CostCenterCode,
|
||||
L.VatCode,
|
||||
L.VatRate
|
||||
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, L.OrderLineID
|
||||
`
|
||||
|
||||
rows, err := db.QueryContext(ctx, q, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var out []OrderLineRawDB
|
||||
|
||||
for rows.Next() {
|
||||
var r OrderLineRawDB
|
||||
err = rows.Scan(
|
||||
&r.OrderLineID,
|
||||
&r.ItemCode,
|
||||
&r.ColorCode,
|
||||
&r.ItemDim1Code,
|
||||
&r.ItemDim2Code,
|
||||
&r.Qty1,
|
||||
&r.Price,
|
||||
&r.DocCurrencyCode,
|
||||
&r.DeliveryDate,
|
||||
&r.LineDescription,
|
||||
&r.UrunAnaGrubu,
|
||||
&r.UrunAltGrubu,
|
||||
&r.IsClosed,
|
||||
&r.WithHoldingTaxType,
|
||||
&r.DOVCode,
|
||||
&r.PlannedDateOfLading,
|
||||
&r.CostCenterCode,
|
||||
&r.VatCode,
|
||||
&r.VatRate,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, r)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
1079
svc/queries/order_write.go
Normal file
1079
svc/queries/order_write.go
Normal file
@@ -0,0 +1,1079 @@
|
||||
// ===================== PART 1 (Satır 1-300) =====================
|
||||
// =======================================================
|
||||
// order_write.go — v4.3 FINAL (Insert + Update + Delete)
|
||||
// - PCTCode her zaman "%0" olarak yazılır (cdPCT FK uyumlu)
|
||||
// - INSERT/UPDATE öncesi ItemVariant Guard + Duplicate Guard (payload içi)
|
||||
// - UpdateOrder: DELETE öncesi child tablolar (trOrderLineCurrency) temizlenir
|
||||
// =======================================================
|
||||
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func nf0(v models.NullFloat64) float64 {
|
||||
if !v.Valid {
|
||||
return 0
|
||||
}
|
||||
return v.Float64
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// COMBO KEY & STRING HELPERS
|
||||
// =======================================================
|
||||
func normalizeComboKey(s string) string {
|
||||
return strings.ToUpper(strings.TrimSpace(s))
|
||||
}
|
||||
|
||||
// makeComboKey: frontend tarafında NullString olmayan struct’lar için
|
||||
func makeComboKey(ln models.OrderDetail) string {
|
||||
model := safeNS(ln.ItemCode)
|
||||
renk := safeNS(ln.ColorCode)
|
||||
renk2 := safeNS(ln.ItemDim2Code)
|
||||
beden := safeNS(ln.ItemDim1Code)
|
||||
|
||||
return normalizeComboKey(
|
||||
fmt.Sprintf("%s||%s||%s||%s", model, renk, beden, renk2),
|
||||
)
|
||||
}
|
||||
|
||||
// qtyValue → NullFloat64 güvenli float64
|
||||
func qtyValue(q models.NullFloat64) float64 {
|
||||
if !q.Valid {
|
||||
return 0
|
||||
}
|
||||
return q.Float64
|
||||
}
|
||||
|
||||
// VatCode: NullString → string
|
||||
// - NULL → ""
|
||||
// - "0" → "" (FK patlamasın, sadece anlamlı kodlar gönderiyoruz)
|
||||
// - "%0", "%10" vb → trimlenmiş hali
|
||||
func normalizeVatCode(ns models.NullString) string {
|
||||
if !ns.Valid {
|
||||
return ""
|
||||
}
|
||||
s := strings.TrimSpace(ns.String)
|
||||
if s == "0" {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// PCTCode: NullString → string
|
||||
// - Artık her durumda "%0" döndürür (tek PCT tipi kullanımı)
|
||||
// NOT: SQL tarafında da "%0" cdPCT içinde tanımlı olmalı
|
||||
func normalizePCTCode(ns models.NullString) string {
|
||||
return "%0"
|
||||
}
|
||||
|
||||
// models.NullString → trimlenmiş string (NULL ise "")
|
||||
func safeNS(ns models.NullString) string {
|
||||
if !ns.Valid {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(ns.String)
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// COMBO KEY HELPERS (DB alanlarından)
|
||||
// =======================================================
|
||||
|
||||
// makeComboKeyParts: düz string alanlardan comboKey üret
|
||||
// comboKey = model || renk || beden || renk2
|
||||
func makeComboKeyParts(item, color, dim1, dim2 string) string {
|
||||
item = strings.TrimSpace(item)
|
||||
color = strings.TrimSpace(color)
|
||||
dim1 = strings.TrimSpace(dim1)
|
||||
dim2 = strings.TrimSpace(dim2)
|
||||
|
||||
if item == "" && color == "" && dim1 == "" && dim2 == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return normalizeComboKey(item + "||" + color + "||" + dim1 + "||" + dim2)
|
||||
}
|
||||
|
||||
// comboFromNulls: NullString alanlardan comboKey üret
|
||||
func comboFromNulls(item, color, dim1, dim2 models.NullString) string {
|
||||
return makeComboKeyParts(
|
||||
safeNS(item),
|
||||
safeNS(color),
|
||||
safeNS(dim1),
|
||||
safeNS(dim2),
|
||||
)
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// ✅ ItemVariant Guard — INSERT / UPDATE öncesi
|
||||
// =======================================================
|
||||
|
||||
// normalizeKeyPart: NullString → trim + UPPER
|
||||
func normalizeKeyPart(ns models.NullString) string {
|
||||
s := strings.TrimSpace(safeNS(ns))
|
||||
return strings.ToUpper(s)
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// AKSBIR DETECTION
|
||||
// =======================================================
|
||||
|
||||
// =======================================================
|
||||
// COMBO KEY BUILDER (AKSBIR AWARE)
|
||||
// =======================================================
|
||||
|
||||
// Variant check: ItemCode + ColorCode + Dim1 + Dim2
|
||||
func ValidateItemVariant(tx *sql.Tx, ln models.OrderDetail) error {
|
||||
fmt.Printf(
|
||||
"🧪 VARIANT GUARD INPUT | ClientKey=%s Item=%q Color=%q Dim1=%q Dim2=%q Dim3=%q Qty1=%v\n",
|
||||
safeNS(ln.ClientKey),
|
||||
safeNS(ln.ItemCode),
|
||||
safeNS(ln.ColorCode),
|
||||
safeNS(ln.ItemDim1Code),
|
||||
safeNS(ln.ItemDim2Code),
|
||||
safeNS(ln.ItemDim3Code),
|
||||
nf0(ln.Qty1),
|
||||
)
|
||||
|
||||
item := normalizeKeyPart(ln.ItemCode)
|
||||
color := normalizeKeyPart(ln.ColorCode)
|
||||
dim1 := normalizeKeyPart(ln.ItemDim1Code)
|
||||
dim2 := normalizeKeyPart(ln.ItemDim2Code)
|
||||
|
||||
// ✅ Placeholder/boş standardizasyon (SENDE "_" geliyor)
|
||||
normalizeEmpty := func(s string) string {
|
||||
s = strings.TrimSpace(strings.ToUpper(s))
|
||||
if s == "_" || s == "-" {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
item = normalizeEmpty(item)
|
||||
color = normalizeEmpty(color)
|
||||
dim1 = normalizeEmpty(dim1)
|
||||
dim2 = normalizeEmpty(dim2)
|
||||
|
||||
if item == "" {
|
||||
return fmt.Errorf(
|
||||
"ItemCode boş olamaz (ClientKey=%s)",
|
||||
safeNS(ln.ClientKey),
|
||||
)
|
||||
fmt.Printf(
|
||||
"🧪 VARIANT NORMALIZED | Item=%q Color=%q Dim1=%q Dim2=%q\n",
|
||||
item, color, dim1, dim2,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
// İ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 (
|
||||
SELECT 1
|
||||
FROM BAGGI_V3.dbo.prItemVariant V WITH (NOLOCK)
|
||||
WHERE ISNULL(LTRIM(RTRIM(V.ItemCode)),'') = @p1
|
||||
AND ISNULL(LTRIM(RTRIM(V.ColorCode)),'') = @p2
|
||||
AND ISNULL(LTRIM(RTRIM(V.ItemDim1Code)),'') = @p3
|
||||
AND ISNULL(LTRIM(RTRIM(V.ItemDim2Code)),'') = @p4
|
||||
) THEN 1 ELSE 0 END
|
||||
`, item, color, dim1, dim2).Scan(&exists)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("ItemVariant kontrol query hatası: %w", err)
|
||||
}
|
||||
|
||||
if exists != 1 {
|
||||
return &models.ValidationError{
|
||||
Code: "INVALID_ITEM_VARIANT",
|
||||
Message: "Tanımsız ürün kombinasyonu",
|
||||
ClientKey: safeNS(ln.ClientKey),
|
||||
ItemCode: item,
|
||||
ColorCode: color,
|
||||
Dim1: dim1,
|
||||
Dim2: dim2,
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateOrderVariants: save/update öncesi payload satırlarını prItemVariant'a göre doğrular.
|
||||
// invalid döner; error sadece DB/prepare/query hatalarında döner.
|
||||
func ValidateOrderVariants(db *sql.DB, lines []models.OrderDetail) ([]models.InvalidVariant, error) {
|
||||
|
||||
normalizeEmpty := func(s string) string {
|
||||
s = strings.TrimSpace(strings.ToUpper(s))
|
||||
if s == "_" || s == "-" {
|
||||
return ""
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
stmt, err := db.Prepare(`
|
||||
SELECT CASE WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM BAGGI_V3.dbo.prItemVariant V WITH (NOLOCK)
|
||||
WHERE ISNULL(LTRIM(RTRIM(V.ItemCode)),'') = @p1
|
||||
AND ISNULL(LTRIM(RTRIM(V.ColorCode)),'') = @p2
|
||||
AND ISNULL(LTRIM(RTRIM(V.ItemDim1Code)),'') = @p3
|
||||
AND ISNULL(LTRIM(RTRIM(V.ItemDim2Code)),'') = @p4
|
||||
) THEN 1 ELSE 0 END
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("validate prepare hatası: %w", err)
|
||||
}
|
||||
defer stmt.Close()
|
||||
|
||||
invalid := make([]models.InvalidVariant, 0)
|
||||
|
||||
for i, ln := range lines {
|
||||
qty := qtyValue(ln.Qty1)
|
||||
if qty <= 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
item := normalizeEmpty(normalizeKeyPart(ln.ItemCode))
|
||||
color := normalizeEmpty(normalizeKeyPart(ln.ColorCode))
|
||||
dim1 := normalizeEmpty(normalizeKeyPart(ln.ItemDim1Code))
|
||||
dim2 := normalizeEmpty(normalizeKeyPart(ln.ItemDim2Code))
|
||||
|
||||
// ItemCode boş ise invalid
|
||||
if strings.TrimSpace(item) == "" {
|
||||
invalid = append(invalid, models.InvalidVariant{
|
||||
Index: i,
|
||||
ClientKey: safeNS(ln.ClientKey),
|
||||
ItemCode: item,
|
||||
ColorCode: color,
|
||||
Dim1: dim1,
|
||||
Dim2: dim2,
|
||||
Qty1: qty,
|
||||
ComboKey: safeNS(ln.ComboKey),
|
||||
Reason: "ItemCode boş",
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
var exists int
|
||||
if err := stmt.QueryRow(item, color, dim1, dim2).Scan(&exists); err != nil {
|
||||
return nil, fmt.Errorf("validate query hatası (i=%d): %w", i, err)
|
||||
}
|
||||
|
||||
if exists != 1 {
|
||||
invalid = append(invalid, models.InvalidVariant{
|
||||
Index: i,
|
||||
ClientKey: safeNS(ln.ClientKey),
|
||||
ItemCode: item,
|
||||
ColorCode: color,
|
||||
Dim1: dim1,
|
||||
Dim2: dim2,
|
||||
Qty1: qty,
|
||||
ComboKey: safeNS(ln.ComboKey),
|
||||
Reason: "prItemVariant’ta yok",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return invalid, nil
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// LineResult → frontend senkronu için
|
||||
// =======================================================
|
||||
|
||||
type OrderLineResult struct {
|
||||
ClientKey string `json:"clientKey"`
|
||||
OrderLineID string `json:"orderLineID"`
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 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) {
|
||||
conn := db.GetDB()
|
||||
|
||||
fmt.Println("🟦 InsertOrder() BAŞLADI -----------------------------")
|
||||
|
||||
tx, err := conn.Begin()
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("tx baslatilamadi: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
now := time.Now()
|
||||
v3User := fmt.Sprintf("V3U%d-%s", user.V3UserGroup, user.V3Username)
|
||||
|
||||
// =======================================================
|
||||
// 1) BACKEND — OrderHeaderID üretimi (HER ZAMAN)
|
||||
// =======================================================
|
||||
realHeaderID := uuid.New().String()
|
||||
|
||||
fmt.Println("🟩 Backend yeni OrderHeaderID üretti:", realHeaderID)
|
||||
|
||||
header.OrderHeaderID = realHeaderID
|
||||
|
||||
// =======================================================
|
||||
// 2) OrderNumber üretimi (LOCAL-* ise gerçek WS numarası)
|
||||
// =======================================================
|
||||
if !header.OrderNumber.Valid ||
|
||||
strings.HasPrefix(header.OrderNumber.String, "LOCAL-") ||
|
||||
len(strings.TrimSpace(header.OrderNumber.String)) == 0 {
|
||||
|
||||
fmt.Println("🟨 LOCAL numara geldi → gerçek WS numarası üretilecek...")
|
||||
|
||||
var realNumber string
|
||||
err := tx.QueryRow(`
|
||||
SELECT CONCAT(
|
||||
'1-WS-3-',
|
||||
RIGHT('00000' + CAST(NEXT VALUE FOR BAGGI_V3.dbo.Seq_OrderNumber_WS AS VARCHAR(10)), 5)
|
||||
)
|
||||
`).Scan(&realNumber)
|
||||
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("Gerçek sipariş numarası üretilemedi: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("🟩 Üretilen gerçek WS numarası:", realNumber)
|
||||
|
||||
header.OrderNumber.String = realNumber
|
||||
header.OrderNumber.Valid = true
|
||||
}
|
||||
|
||||
newID := realHeaderID // artık DB’ye bu yazılacak
|
||||
|
||||
// =======================================================
|
||||
// 3) Döviz kuru çözümü
|
||||
// =======================================================
|
||||
exRate := 1.0
|
||||
if header.DocCurrencyCode.Valid && header.DocCurrencyCode.String != "TRY" {
|
||||
if c, err := GetTodayCurrencyV3(conn, header.DocCurrencyCode.String); err == nil {
|
||||
if c.Rate > 0 {
|
||||
exRate = c.Rate
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 4) HEADER INSERT
|
||||
// =======================================================
|
||||
|
||||
queryHeader := `
|
||||
INSERT INTO BAGGI_V3.dbo.trOrderHeader (
|
||||
OrderHeaderID, OrderTypeCode, ProcessCode, OrderNumber, IsCancelOrder,
|
||||
OrderDate, OrderTime, DocumentNumber, PaymentTerm,
|
||||
AverageDueDate, Description, InternalDescription,
|
||||
CurrAccTypeCode, CurrAccCode, SubCurrAccID, ContactID,
|
||||
ShipmentMethodCode, ShippingPostalAddressID, BillingPostalAddressID,
|
||||
GuarantorContactID, GuarantorContactID2, RoundsmanCode,
|
||||
DeliveryCompanyCode, TaxTypeCode, WithHoldingTaxTypeCode, DOVCode,
|
||||
TaxExemptionCode, CompanyCode, OfficeCode, StoreTypeCode, StoreCode,
|
||||
POSTerminalID, WarehouseCode, ToWarehouseCode,
|
||||
OrdererCompanyCode, OrdererOfficeCode, OrdererStoreCode,
|
||||
GLTypeCode, DocCurrencyCode, LocalCurrencyCode, ExchangeRate,
|
||||
TDisRate1, TDisRate2, TDisRate3, TDisRate4, TDisRate5,
|
||||
DiscountReasonCode, SurplusOrderQtyToleranceRate,
|
||||
ImportFileNumber, ExportFileNumber,
|
||||
IncotermCode1, IncotermCode2, LettersOfCreditNumber,
|
||||
PaymentMethodCode, IsInclutedVat, IsCreditSale, IsCreditableConfirmed,
|
||||
CreditableConfirmedUser, CreditableConfirmedDate,
|
||||
IsSalesViaInternet, IsSuspended, IsCompleted, IsPrinted,
|
||||
IsLocked, UserLocked, IsClosed,
|
||||
ApplicationCode, ApplicationID,
|
||||
CreatedUserName, CreatedDate, LastUpdatedUserName, LastUpdatedDate,
|
||||
IsProposalBased
|
||||
)
|
||||
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,
|
||||
@p46,@p47,
|
||||
@p48,@p49,
|
||||
@p50,@p51,@p52,
|
||||
@p53,@p54,@p55,@p56,@p57,
|
||||
@p58,@p59,@p60,@p61,@p62,
|
||||
@p63,@p64,@p65,
|
||||
@p66,@p67,@p68,@p69,@p70,@p71,@p72,@p73
|
||||
);
|
||||
`
|
||||
|
||||
fmt.Println("🟪 HEADER INSERT ÇALIŞIYOR...")
|
||||
|
||||
headerParams := []any{
|
||||
header.OrderHeaderID,
|
||||
nullableInt16(header.OrderTypeCode, 1),
|
||||
nullableString(header.ProcessCode, "WS"),
|
||||
nullableString(header.OrderNumber, ""),
|
||||
nullableBool(header.IsCancelOrder, false),
|
||||
|
||||
nullableDateString(header.OrderDate),
|
||||
nullableTimeString(header.OrderTime),
|
||||
nullableString(header.DocumentNumber, ""),
|
||||
nullableInt16(header.PaymentTerm, 0),
|
||||
|
||||
nullableDateString(header.AverageDueDate),
|
||||
nullableString(header.Description, ""),
|
||||
nullableString(header.InternalDescription, ""),
|
||||
|
||||
nullableInt16(header.CurrAccTypeCode, 0),
|
||||
nullableString(header.CurrAccCode, ""),
|
||||
nullableUUID(header.SubCurrAccID),
|
||||
nullableUUID(header.ContactID),
|
||||
|
||||
nullableString(header.ShipmentMethodCode, ""),
|
||||
nullableUUID(header.ShippingPostalAddressID),
|
||||
nullableUUID(header.BillingPostalAddressID),
|
||||
|
||||
nullableUUID(header.GuarantorContactID),
|
||||
nullableUUID(header.GuarantorContactID2),
|
||||
nullableString(header.RoundsmanCode, ""),
|
||||
|
||||
nullableString(header.DeliveryCompanyCode, ""),
|
||||
nullableInt16(header.TaxTypeCode, 0),
|
||||
nullableString(header.WithHoldingTaxTypeCode, ""),
|
||||
nullableString(header.DOVCode, ""),
|
||||
|
||||
nullableInt16(header.TaxExemptionCode, 0),
|
||||
nullableInt32ToInt16(header.CompanyCode, 1),
|
||||
nullableString(header.OfficeCode, "101"),
|
||||
nullableInt16(header.StoreTypeCode, 5),
|
||||
nullableString(header.StoreCode, ""),
|
||||
|
||||
nullableInt16(header.POSTerminalID, 0),
|
||||
nullableString(header.WarehouseCode, "1-0-12"),
|
||||
nullableString(header.ToWarehouseCode, ""),
|
||||
|
||||
nullableInt32ToInt16(header.OrdererCompanyCode, 1),
|
||||
nullableString(header.OrdererOfficeCode, "101"),
|
||||
nullableString(header.OrdererStoreCode, ""),
|
||||
|
||||
nullableString(header.GLTypeCode, ""),
|
||||
nullableString(header.DocCurrencyCode, "TRY"),
|
||||
nullableString(header.LocalCurrencyCode, "TRY"),
|
||||
nullableFloat64(header.ExchangeRate, exRate),
|
||||
|
||||
nullableFloat64(header.TDisRate1, 0),
|
||||
nullableFloat64(header.TDisRate2, 0),
|
||||
nullableFloat64(header.TDisRate3, 0),
|
||||
nullableFloat64(header.TDisRate4, 0),
|
||||
nullableFloat64(header.TDisRate5, 0),
|
||||
|
||||
nullableInt16(header.DiscountReasonCode, 0),
|
||||
nullableFloat64(header.SurplusOrderQtyToleranceRate, 0),
|
||||
|
||||
nullableString(header.ImportFileNumber, ""),
|
||||
nullableString(header.ExportFileNumber, ""),
|
||||
|
||||
nullableString(header.IncotermCode1, ""),
|
||||
nullableString(header.IncotermCode2, ""),
|
||||
nullableString(header.LettersOfCreditNumber, ""),
|
||||
|
||||
nullableString(header.PaymentMethodCode, ""),
|
||||
nullableBool(header.IsInclutedVat, false),
|
||||
nullableBool(header.IsCreditSale, true),
|
||||
nullableBool(header.IsCreditableConfirmed, true),
|
||||
|
||||
nullableString(header.CreditableConfirmedUser, v3User),
|
||||
nullableDateTime(header.CreditableConfirmedDate, now),
|
||||
|
||||
nullableBool(header.IsSalesViaInternet, false),
|
||||
nullableBool(header.IsSuspended, false),
|
||||
nullableBool(header.IsCompleted, false),
|
||||
nullableBool(header.IsPrinted, false),
|
||||
|
||||
nullableBool(header.IsLocked, false),
|
||||
nullableBool(header.UserLocked, false),
|
||||
nullableBool(header.IsClosed, false),
|
||||
|
||||
nullableString(header.ApplicationCode, "Order"),
|
||||
nullableUUID(header.ApplicationID),
|
||||
|
||||
nullableString(header.CreatedUserName, v3User),
|
||||
nullableDateTimeString(header.CreatedDate, now),
|
||||
nullableString(header.LastUpdatedUserName, v3User),
|
||||
nullableDateTimeString(header.LastUpdatedDate, now),
|
||||
|
||||
nullableBool(header.IsProposalBased, false),
|
||||
}
|
||||
|
||||
if _, err := tx.Exec(queryHeader, headerParams...); err != nil {
|
||||
fmt.Println("❌ HEADER INSERT ERROR:", err)
|
||||
return "", nil, fmt.Errorf("header insert hatasi: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("🟩 HEADER INSERT OK — ID:", newID)
|
||||
|
||||
// =======================================================
|
||||
// 5) LINE INSERT
|
||||
// =======================================================
|
||||
|
||||
insStmt, err := tx.Prepare(`
|
||||
INSERT INTO BAGGI_V3.dbo.trOrderLine (
|
||||
OrderLineID,
|
||||
SortOrder, ItemTypeCode, ItemCode, ColorCode,
|
||||
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, Qty2,
|
||||
CancelQty1, CancelQty2,
|
||||
OrderCancelReasonCode,
|
||||
DeliveryDate, PlannedDateOfLading,
|
||||
LineDescription,
|
||||
UsedBarcode, CostCenterCode,
|
||||
VatCode, VatRate, PCTCode, PCTRate,
|
||||
LDisRate1, LDisRate2, LDisRate3, LDisRate4, LDisRate5,
|
||||
DocCurrencyCode, PriceCurrencyCode, PriceExchangeRate,
|
||||
Price,
|
||||
BaseProcessCode, BaseOrderNumber,
|
||||
BaseCustomerTypeCode, BaseCustomerCode,
|
||||
BaseSubCurrAccID, BaseStoreCode,
|
||||
OrderHeaderID,
|
||||
CreatedUserName, CreatedDate,
|
||||
LastUpdatedUserName, LastUpdatedDate,
|
||||
SurplusOrderQtyToleranceRate,
|
||||
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
|
||||
)`)
|
||||
if err != nil {
|
||||
return "", nil, fmt.Errorf("line insert stmt hazirlanamadi: %w", err)
|
||||
}
|
||||
defer insStmt.Close()
|
||||
|
||||
lineResults := make([]OrderLineResult, 0, len(lines))
|
||||
|
||||
// ✅ Duplicate Guard (payload içi)
|
||||
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)
|
||||
|
||||
// Her satır için yeni GUID
|
||||
if ln.OrderLineID == "" {
|
||||
newLineID := uuid.New().String()
|
||||
fmt.Println("🆕 Yeni LineID üretildi:", newLineID)
|
||||
ln.OrderLineID = newLineID
|
||||
}
|
||||
|
||||
// ✅ Duplicate Guard (comboKey)
|
||||
comboKey := normalizeComboKey(safeNS(ln.ComboKey))
|
||||
if comboKey == "" {
|
||||
comboKey = makeComboKey(ln)
|
||||
}
|
||||
|
||||
if qtyValue(ln.Qty1) > 0 && comboKey != "" {
|
||||
if seenCombo[comboKey] {
|
||||
return "", nil, fmt.Errorf(
|
||||
"Duplicate satır (comboKey=%s, ClientKey=%s)",
|
||||
comboKey,
|
||||
safeNS(ln.ClientKey),
|
||||
)
|
||||
}
|
||||
seenCombo[comboKey] = true
|
||||
}
|
||||
|
||||
// V2 Logic → %0 PCT
|
||||
vatCode := normalizeVatCode(ln.VatCode)
|
||||
pctCode := normalizePCTCode(ln.PCTCode)
|
||||
|
||||
var pctParam any
|
||||
if pctCode == "" {
|
||||
pctParam = nil
|
||||
} else {
|
||||
pctParam = pctCode
|
||||
}
|
||||
|
||||
planned := nullableDateString(ln.PlannedDateOfLading)
|
||||
// ✅ INSERT ÖNCESİ ItemVariant GUARD
|
||||
if qtyValue(ln.Qty1) > 0 {
|
||||
if err := ValidateItemVariant(tx, ln); err != nil {
|
||||
fmt.Println("❌ VARIANT GUARD (INSERT):", err)
|
||||
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,
|
||||
ln.OrderLineID,
|
||||
safeNS(ln.ClientKey),
|
||||
safeNS(ln.ItemCode),
|
||||
safeNS(ln.ColorCode),
|
||||
safeNS(ln.ItemDim1Code),
|
||||
safeNS(ln.ItemDim2Code),
|
||||
safeNS(ln.ItemDim3Code),
|
||||
nf0(ln.Qty1),
|
||||
)
|
||||
|
||||
_, err := insStmt.Exec(
|
||||
ln.OrderLineID,
|
||||
ln.SortOrder,
|
||||
ln.ItemTypeCode,
|
||||
nullableString(ln.ItemCode, ""),
|
||||
safeNS(ln.ColorCode),
|
||||
safeNS(ln.ItemDim1Code),
|
||||
safeNS(ln.ItemDim2Code),
|
||||
nullableString(ln.ItemDim3Code, ""),
|
||||
ln.Qty1, ln.Qty2,
|
||||
ln.CancelQty1, ln.CancelQty2,
|
||||
nullableString(ln.OrderCancelReasonCode, ""),
|
||||
nullableTime(ln.DeliveryDate, now),
|
||||
planned,
|
||||
nullableString(ln.LineDescription, ""),
|
||||
nullableString(ln.UsedBarcode, ""),
|
||||
nullableString(ln.CostCenterCode, ""),
|
||||
vatCode, nf0(ln.VatRate),
|
||||
pctParam, nf0(ln.PCTRate),
|
||||
nf0(ln.LDisRate1),
|
||||
nf0(ln.LDisRate2),
|
||||
nf0(ln.LDisRate3),
|
||||
nf0(ln.LDisRate4),
|
||||
nf0(ln.LDisRate5),
|
||||
nullableString(ln.DocCurrencyCode, "TRY"),
|
||||
nullableString(ln.PriceCurrencyCode, "TRY"),
|
||||
nf0(ln.PriceExchangeRate),
|
||||
nf0(ln.Price),
|
||||
nullableString(ln.BaseProcessCode, ""),
|
||||
nullableString(ln.BaseOrderNumber, ""),
|
||||
ln.BaseCustomerTypeCode,
|
||||
nullableString(ln.BaseCustomerCode, ""),
|
||||
nullableUUID(ln.BaseSubCurrAccID),
|
||||
nullableString(ln.BaseStoreCode, ""),
|
||||
header.OrderHeaderID,
|
||||
v3User, now,
|
||||
v3User, now,
|
||||
nf0(ln.SurplusOrderQtyToleranceRate),
|
||||
nullableString(ln.WithHoldingTaxTypeCode, ""),
|
||||
nullableString(ln.DOVCode, ""),
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
fmt.Println("❌ INSERT LINE ERROR")
|
||||
fmt.Printf(
|
||||
"💥 FAILED LINE | LineID=%s ClientKey=%s Item=%q Color=%q Dim1=%q Dim2=%q Dim3=%q\n",
|
||||
ln.OrderLineID,
|
||||
safeNS(ln.ClientKey),
|
||||
safeNS(ln.ItemCode),
|
||||
safeNS(ln.ColorCode),
|
||||
safeNS(ln.ItemDim1Code),
|
||||
safeNS(ln.ItemDim2Code),
|
||||
safeNS(ln.ItemDim3Code),
|
||||
)
|
||||
fmt.Println("SQL ERROR:", err)
|
||||
return "", nil, fmt.Errorf("line insert hatasi: %w", err)
|
||||
}
|
||||
|
||||
if ln.ClientKey.Valid && ln.ClientKey.String != "" {
|
||||
lineResults = append(lineResults, OrderLineResult{
|
||||
ClientKey: ln.ClientKey.String,
|
||||
OrderLineID: ln.OrderLineID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 6) COMMIT
|
||||
// =======================================================
|
||||
fmt.Println("🟨 COMMIT EDİLİYOR...")
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
fmt.Println("❌ COMMIT ERROR:", err)
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
fmt.Println("✅ COMMIT OK — INSERT ORDER BİTTİ")
|
||||
fmt.Println("────────────────────────────────────")
|
||||
|
||||
return newID, lineResults, nil
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 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.Println("══════════════════════════════════════")
|
||||
|
||||
tx, err := conn.Begin()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tx baslatilamadi: %w", err)
|
||||
}
|
||||
defer tx.Rollback()
|
||||
|
||||
now := time.Now()
|
||||
v3User := fmt.Sprintf("V3U%d-%s", user.V3UserGroup, user.V3Username)
|
||||
|
||||
// Döviz kuru
|
||||
exRate := 1.0
|
||||
if header.DocCurrencyCode.Valid && header.DocCurrencyCode.String != "TRY" {
|
||||
if c, err := GetTodayCurrencyV3(conn, header.DocCurrencyCode.String); err == nil && c.Rate > 0 {
|
||||
exRate = c.Rate
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 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)
|
||||
|
||||
rows, err := tx.Query(`
|
||||
SELECT
|
||||
CONVERT(varchar(36), OrderLineID),
|
||||
ISNULL(IsClosed, 0),
|
||||
ISNULL(ItemCode,''),
|
||||
ISNULL(ColorCode,''),
|
||||
ISNULL(ItemDim1Code,''),
|
||||
ISNULL(ItemDim2Code,'')
|
||||
FROM BAGGI_V3.dbo.trOrderLine
|
||||
WHERE OrderHeaderID=@p1
|
||||
`, header.OrderHeaderID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mevcut satirlar okunamadi: %w", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var id, item, color, dim1, dim2 string
|
||||
var closed bool
|
||||
|
||||
if err := rows.Scan(&id, &closed, &item, &color, &dim1, &dim2); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
combo := makeComboKeyParts(item, color, dim1, dim2)
|
||||
|
||||
if closed {
|
||||
existingClosed[id] = true
|
||||
if combo != "" {
|
||||
existingClosedCombo[combo] = id
|
||||
}
|
||||
} else {
|
||||
existingOpen[id] = true
|
||||
if combo != "" {
|
||||
existingOpenCombo[combo] = id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// HEADER UPDATE
|
||||
// ======================================================
|
||||
|
||||
_, err = tx.Exec(`
|
||||
UPDATE BAGGI_V3.dbo.trOrderHeader SET
|
||||
OrderDate=@p1,
|
||||
OrderTime=@p2,
|
||||
AverageDueDate=@p3,
|
||||
Description=@p4,
|
||||
InternalDescription=@p5,
|
||||
DocCurrencyCode=@p6,
|
||||
LocalCurrencyCode=@p7,
|
||||
ExchangeRate=@p8,
|
||||
LastUpdatedUserName=@p9,
|
||||
LastUpdatedDate=@p10
|
||||
WHERE OrderHeaderID=@p11
|
||||
`,
|
||||
nullableDateString(header.OrderDate),
|
||||
nullableTimeString(header.OrderTime),
|
||||
nullableDateString(header.AverageDueDate),
|
||||
nullableString(header.Description, ""),
|
||||
nullableString(header.InternalDescription, ""),
|
||||
nullableString(header.DocCurrencyCode, "TRY"),
|
||||
nullableString(header.LocalCurrencyCode, "TRY"),
|
||||
nullableFloat64(header.ExchangeRate, exRate),
|
||||
v3User,
|
||||
now,
|
||||
header.OrderHeaderID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// ======================================================
|
||||
// PREPARE STATEMENTS
|
||||
// ======================================================
|
||||
|
||||
insStmt, err := tx.Prepare(`INSERT INTO BAGGI_V3.dbo.trOrderLine (
|
||||
OrderLineID, SortOrder, ItemTypeCode, ItemCode, ColorCode,
|
||||
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, Qty2, CancelQty1, CancelQty2, OrderCancelReasonCode,
|
||||
DeliveryDate, PlannedDateOfLading, LineDescription,
|
||||
UsedBarcode, CostCenterCode,
|
||||
VatCode, VatRate, PCTCode, PCTRate,
|
||||
LDisRate1, LDisRate2, LDisRate3, LDisRate4, LDisRate5,
|
||||
DocCurrencyCode, PriceCurrencyCode, PriceExchangeRate,
|
||||
Price, BaseProcessCode, BaseOrderNumber,
|
||||
BaseCustomerTypeCode, BaseCustomerCode,
|
||||
BaseSubCurrAccID, BaseStoreCode,
|
||||
OrderHeaderID, CreatedUserName, CreatedDate,
|
||||
LastUpdatedUserName, LastUpdatedDate,
|
||||
SurplusOrderQtyToleranceRate,
|
||||
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)`)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer insStmt.Close()
|
||||
|
||||
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,
|
||||
OrderCancelReasonCode=@p12,
|
||||
DeliveryDate=@p13, PlannedDateOfLading=@p14,
|
||||
LineDescription=@p15, UsedBarcode=@p16, CostCenterCode=@p17,
|
||||
VatCode=@p18, VatRate=@p19, PCTCode=@p20, PCTRate=@p21,
|
||||
LDisRate1=@p22, LDisRate2=@p23, LDisRate3=@p24,
|
||||
LDisRate4=@p25, LDisRate5=@p26,
|
||||
DocCurrencyCode=@p27, PriceCurrencyCode=@p28,
|
||||
PriceExchangeRate=@p29, Price=@p30,
|
||||
BaseProcessCode=@p31, BaseOrderNumber=@p32,
|
||||
BaseCustomerTypeCode=@p33, BaseCustomerCode=@p34,
|
||||
BaseSubCurrAccID=@p35, BaseStoreCode=@p36,
|
||||
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()
|
||||
|
||||
lineResults := make([]OrderLineResult, 0)
|
||||
seenCombo := make(map[string]bool)
|
||||
|
||||
for _, ln := range lines {
|
||||
|
||||
comboKey := normalizeComboKey(safeNS(ln.ComboKey))
|
||||
if comboKey == "" {
|
||||
comboKey = makeComboKey(ln)
|
||||
}
|
||||
|
||||
// Duplicate guard (SADECE aktif)
|
||||
if qtyValue(ln.Qty1) > 0 && comboKey != "" {
|
||||
if seenCombo[comboKey] {
|
||||
return nil, fmt.Errorf("Duplicate satır: %s", comboKey)
|
||||
}
|
||||
seenCombo[comboKey] = true
|
||||
}
|
||||
|
||||
// Kapalı satır
|
||||
if ln.OrderLineID != "" && existingClosed[ln.OrderLineID] {
|
||||
continue
|
||||
}
|
||||
if comboKey != "" {
|
||||
if _, ok := existingClosedCombo[comboKey]; ok {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// DELETE SIGNAL
|
||||
if ln.OrderLineID != "" && qtyValue(ln.Qty1) <= 0 {
|
||||
_, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, ln.OrderLineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, 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 {
|
||||
return nil, err
|
||||
}
|
||||
delete(existingOpen, ln.OrderLineID)
|
||||
delete(existingOpenCombo, comboKey)
|
||||
continue
|
||||
}
|
||||
isNew := false
|
||||
|
||||
if ln.OrderLineID == "" {
|
||||
if dbID, ok := existingOpenCombo[comboKey]; ok {
|
||||
ln.OrderLineID = dbID
|
||||
} else {
|
||||
ln.OrderLineID = uuid.New().String()
|
||||
isNew = true
|
||||
}
|
||||
}
|
||||
|
||||
if qtyValue(ln.Qty1) > 0 {
|
||||
if err := ValidateItemVariant(tx, ln); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if isNew {
|
||||
_, err := insStmt.Exec(
|
||||
ln.OrderLineID, ln.SortOrder, ln.ItemTypeCode,
|
||||
nullableString(ln.ItemCode, ""), safeNS(ln.ColorCode),
|
||||
safeNS(ln.ItemDim1Code), safeNS(ln.ItemDim2Code),
|
||||
nullableString(ln.ItemDim3Code, ""),
|
||||
ln.Qty1, ln.Qty2, ln.CancelQty1, ln.CancelQty2,
|
||||
nullableString(ln.OrderCancelReasonCode, ""),
|
||||
nullableTime(ln.DeliveryDate, now),
|
||||
nullableDateString(ln.PlannedDateOfLading),
|
||||
nullableString(ln.LineDescription, ""),
|
||||
nullableString(ln.UsedBarcode, ""),
|
||||
nullableString(ln.CostCenterCode, ""),
|
||||
normalizeVatCode(ln.VatCode), nf0(ln.VatRate),
|
||||
normalizePCTCode(ln.PCTCode), nf0(ln.PCTRate),
|
||||
nf0(ln.LDisRate1), nf0(ln.LDisRate2),
|
||||
nf0(ln.LDisRate3), nf0(ln.LDisRate4), nf0(ln.LDisRate5),
|
||||
nullableString(ln.DocCurrencyCode, "TRY"),
|
||||
nullableString(ln.PriceCurrencyCode, "TRY"),
|
||||
nf0(ln.PriceExchangeRate), nf0(ln.Price),
|
||||
nullableString(ln.BaseProcessCode, ""),
|
||||
nullableString(ln.BaseOrderNumber, ""),
|
||||
ln.BaseCustomerTypeCode,
|
||||
nullableString(ln.BaseCustomerCode, ""),
|
||||
nullableUUID(ln.BaseSubCurrAccID),
|
||||
nullableString(ln.BaseStoreCode, ""),
|
||||
header.OrderHeaderID,
|
||||
v3User, now, v3User, now,
|
||||
nf0(ln.SurplusOrderQtyToleranceRate),
|
||||
nullableString(ln.WithHoldingTaxTypeCode, ""),
|
||||
nullableString(ln.DOVCode, ""),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
_, err := updStmt.Exec(
|
||||
ln.SortOrder, ln.ItemTypeCode,
|
||||
nullableString(ln.ItemCode, ""),
|
||||
safeNS(ln.ColorCode),
|
||||
safeNS(ln.ItemDim1Code),
|
||||
safeNS(ln.ItemDim2Code),
|
||||
nullableString(ln.ItemDim3Code, ""),
|
||||
nf0(ln.Qty1), nf0(ln.Qty2),
|
||||
nf0(ln.CancelQty1), nf0(ln.CancelQty2),
|
||||
nullableString(ln.OrderCancelReasonCode, ""),
|
||||
nullableTime(ln.DeliveryDate, now),
|
||||
nullableDateString(ln.PlannedDateOfLading),
|
||||
nullableString(ln.LineDescription, ""),
|
||||
nullableString(ln.UsedBarcode, ""),
|
||||
nullableString(ln.CostCenterCode, ""),
|
||||
normalizeVatCode(ln.VatCode), nf0(ln.VatRate),
|
||||
normalizePCTCode(ln.PCTCode), nf0(ln.PCTRate),
|
||||
nf0(ln.LDisRate1), nf0(ln.LDisRate2),
|
||||
nf0(ln.LDisRate3), nf0(ln.LDisRate4), nf0(ln.LDisRate5),
|
||||
nullableString(ln.DocCurrencyCode, "TRY"),
|
||||
nullableString(ln.PriceCurrencyCode, "TRY"),
|
||||
nf0(ln.PriceExchangeRate), nf0(ln.Price),
|
||||
nullableString(ln.BaseProcessCode, ""),
|
||||
nullableString(ln.BaseOrderNumber, ""),
|
||||
ln.BaseCustomerTypeCode,
|
||||
nullableString(ln.BaseCustomerCode, ""),
|
||||
nullableUUID(ln.BaseSubCurrAccID),
|
||||
nullableString(ln.BaseStoreCode, ""),
|
||||
v3User, now,
|
||||
nf0(ln.SurplusOrderQtyToleranceRate),
|
||||
nullableString(ln.WithHoldingTaxTypeCode, ""),
|
||||
nullableString(ln.DOVCode, ""),
|
||||
ln.OrderLineID,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
delete(existingOpen, ln.OrderLineID)
|
||||
delete(existingOpenCombo, comboKey)
|
||||
|
||||
if ln.ClientKey.Valid {
|
||||
lineResults = append(lineResults, OrderLineResult{
|
||||
ClientKey: ln.ClientKey.String,
|
||||
OrderLineID: ln.OrderLineID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Grid dışı kalan açık satırlar
|
||||
for id := range existingOpen {
|
||||
_, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, 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 {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return lineResults, nil
|
||||
}
|
||||
109
svc/queries/orderinventory.go
Normal file
109
svc/queries/orderinventory.go
Normal file
@@ -0,0 +1,109 @@
|
||||
package queries
|
||||
|
||||
// 🔹 Sipariş ekranı stok kontrolü (ürün + renk + 2. renk + beden)
|
||||
// 🔧 Renk1 boşsa (aksesuar vb.) renksiz stokları da çeker
|
||||
const GetOrderInventory = `
|
||||
DECLARE @ProductCode NVARCHAR(30) = @p1;
|
||||
DECLARE @ColorCode NVARCHAR(30) = @p2;
|
||||
DECLARE @ColorCode2 NVARCHAR(30) = @p3;
|
||||
|
||||
-- 🔧 Normalize giriş parametreleri
|
||||
SET @ColorCode = LTRIM(RTRIM(ISNULL(@ColorCode, '')));
|
||||
SET @ColorCode2 = LTRIM(RTRIM(ISNULL(@ColorCode2, '')));
|
||||
|
||||
IF @ColorCode = ''
|
||||
BEGIN
|
||||
SET @ColorCode = '';
|
||||
END
|
||||
|
||||
IF @ColorCode2 = ''
|
||||
BEGIN
|
||||
SET @ColorCode2 = NULL;
|
||||
END
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 🔹 Ana sorgu
|
||||
------------------------------------------------------------
|
||||
SELECT
|
||||
Inventory.ItemCode AS Urun_Kodu,
|
||||
Inventory.ColorCode AS Renk_Kodu,
|
||||
ISNULL((
|
||||
SELECT TOP 1 ColorDescription
|
||||
FROM cdColorDesc WITH(NOLOCK)
|
||||
WHERE cdColorDesc.ColorCode = Inventory.ColorCode
|
||||
AND cdColorDesc.LangCode = N'TR'
|
||||
), '') AS Renk_Aciklamasi,
|
||||
|
||||
-- ✅ NULL bedenleri boş string olarak getir
|
||||
ISNULL(Inventory.ItemDim1Code, '') AS Beden,
|
||||
ISNULL(Inventory.ItemDim2Code, '') AS Yaka,
|
||||
|
||||
ROUND(
|
||||
SUM(Inventory.InventoryQty1) -
|
||||
(SUM(Inventory.ReserveQty1) + SUM(Inventory.DispOrderQty1) + SUM(Inventory.PickingQty1)),
|
||||
cdUnitOfMeasure.RoundDigit
|
||||
) AS Kullanilabilir_Envanter
|
||||
|
||||
FROM cdItem WITH (NOLOCK)
|
||||
JOIN cdUnitOfMeasure WITH (NOLOCK)
|
||||
ON cdItem.UnitOfMeasureCode1 = cdUnitOfMeasure.UnitOfMeasureCode
|
||||
JOIN (
|
||||
SELECT
|
||||
CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
SUM(CASE WHEN SourceTable = 'PickingStates' THEN Qty1 ELSE 0 END) AS PickingQty1,
|
||||
SUM(CASE WHEN SourceTable = 'ReserveStates' THEN Qty1 ELSE 0 END) AS ReserveQty1,
|
||||
SUM(CASE WHEN SourceTable = 'DispOrderStates' THEN Qty1 ELSE 0 END) AS DispOrderQty1,
|
||||
SUM(CASE WHEN SourceTable = 'trStock' THEN (In_Qty1 - Out_Qty1) ELSE 0 END) AS InventoryQty1
|
||||
FROM (
|
||||
SELECT 'PickingStates' AS SourceTable, CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, 0 AS In_Qty1, 0 AS Out_Qty1
|
||||
FROM PickingStates
|
||||
UNION ALL
|
||||
SELECT 'ReserveStates', CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, 0, 0
|
||||
FROM ReserveStates
|
||||
UNION ALL
|
||||
SELECT 'DispOrderStates', CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, 0, 0
|
||||
FROM DispOrderStates
|
||||
UNION ALL
|
||||
SELECT 'trStock', CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
0, SUM(In_Qty1), SUM(Out_Qty1)
|
||||
FROM trStock WITH (NOLOCK)
|
||||
GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||
) AS SourceData
|
||||
GROUP BY CompanyCode, OfficeCode, StoreTypeCode, StoreCode, WarehouseCode,
|
||||
ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code
|
||||
) AS Inventory
|
||||
ON cdItem.ItemTypeCode = Inventory.ItemTypeCode
|
||||
AND cdItem.ItemCode = Inventory.ItemCode
|
||||
LEFT JOIN ProductAttributesFilter
|
||||
ON ProductAttributesFilter.ItemCode = Inventory.ItemCode
|
||||
WHERE
|
||||
Inventory.ItemTypeCode IN (1)
|
||||
AND Inventory.WarehouseCode IN ('1-0-12','1-0-21','1-0-10','1-0-2','1-1-3','1-2-4','1-2-5')
|
||||
AND cdItem.IsBlocked = 0
|
||||
AND Inventory.ItemCode = @ProductCode
|
||||
AND (
|
||||
-- 🔹 Eğer renk girilmişse o renk; boşsa renksiz stokları getir
|
||||
(LTRIM(RTRIM(@ColorCode)) <> '' AND Inventory.ColorCode = @ColorCode)
|
||||
OR (LTRIM(RTRIM(@ColorCode)) = '' AND (Inventory.ColorCode IS NULL OR LTRIM(RTRIM(Inventory.ColorCode)) = ''))
|
||||
)
|
||||
AND (
|
||||
-- 🔹 2. renk sadece doluysa filtrelensin
|
||||
@ColorCode2 IS NULL
|
||||
OR Inventory.ItemDim2Code = @ColorCode2
|
||||
)
|
||||
GROUP BY
|
||||
Inventory.ItemCode,
|
||||
Inventory.ColorCode,
|
||||
ISNULL(Inventory.ItemDim1Code, ''), -- ✅ NULL bedenleri boş string olarak gruplar
|
||||
ISNULL(Inventory.ItemDim2Code, ''), -- ✅ NULL yakaları boş string olarak gruplar
|
||||
cdUnitOfMeasure.RoundDigit;
|
||||
`
|
||||
213
svc/queries/orderlist.go
Normal file
213
svc/queries/orderlist.go
Normal file
@@ -0,0 +1,213 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/auth"
|
||||
"bssapp-backend/internal/authz"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ========================================================
|
||||
// 📌 GetOrderList — FINAL + CURRENCY SAFE + PIYASA AUTHZ
|
||||
// ========================================================
|
||||
func GetOrderList(
|
||||
ctx context.Context,
|
||||
mssql *sql.DB,
|
||||
pg *sql.DB,
|
||||
search string,
|
||||
) (*sql.Rows, error) {
|
||||
|
||||
claims, ok := auth.GetClaimsFromContext(ctx)
|
||||
if !ok || claims == nil {
|
||||
return nil, fmt.Errorf("unauthorized: claims not found")
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 🔐 PIYASA FILTER (ADMIN BYPASS)
|
||||
// ----------------------------------------------------
|
||||
piyasaWhere := "1=1"
|
||||
|
||||
if !claims.IsAdmin() {
|
||||
|
||||
codes, err := authz.GetUserPiyasaCodes(pg, int(claims.ID))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("piyasa codes load error: %w", err)
|
||||
}
|
||||
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 📄 BASE QUERY
|
||||
// ----------------------------------------------------
|
||||
baseQuery := fmt.Sprintf(`
|
||||
SELECT
|
||||
CAST(h.OrderHeaderID AS NVARCHAR(50)) AS OrderHeaderID,
|
||||
ISNULL(h.OrderNumber, '') AS OrderNumber,
|
||||
CONVERT(varchar, h.OrderDate, 23) AS OrderDate,
|
||||
|
||||
ISNULL(h.CurrAccCode, '') AS CurrAccCode,
|
||||
ISNULL(ca.CurrAccDescription, '') AS CurrAccDescription,
|
||||
|
||||
ISNULL(mt.AttributeDescription, '') AS MusteriTemsilcisi,
|
||||
ISNULL(py.AttributeDescription, '') AS Piyasa,
|
||||
|
||||
CONVERT(varchar, h.CreditableConfirmedDate,23) AS CreditableConfirmedDate,
|
||||
ISNULL(h.DocCurrencyCode,'TRY') AS DocCurrencyCode,
|
||||
|
||||
ISNULL(l.TotalAmount,0) AS TotalAmount,
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- USD HESABI (TRY / EUR / GBP / USD DESTEKLİ)
|
||||
----------------------------------------------------------------
|
||||
CASE
|
||||
WHEN h.DocCurrencyCode = 'USD'
|
||||
THEN ISNULL(l.TotalAmount,0)
|
||||
|
||||
WHEN h.DocCurrencyCode = 'TRY'
|
||||
AND usd.Rate > 0
|
||||
THEN ISNULL(l.TotalAmount,0) / usd.Rate
|
||||
|
||||
WHEN h.DocCurrencyCode IN ('EUR','GBP')
|
||||
AND cur.Rate > 0
|
||||
AND usd.Rate > 0
|
||||
THEN (ISNULL(l.TotalAmount,0) * cur.Rate) / usd.Rate
|
||||
|
||||
ELSE 0
|
||||
END AS TotalAmountUSD,
|
||||
|
||||
ISNULL(h.IsCreditableConfirmed,0) AS IsCreditableConfirmed,
|
||||
ISNULL(h.Description,'') AS Description,
|
||||
|
||||
usd.Rate AS ExchangeRateUSD
|
||||
|
||||
FROM dbo.trOrderHeader h
|
||||
|
||||
JOIN (
|
||||
SELECT
|
||||
OrderHeaderID,
|
||||
SUM(Qty1 * Price) AS TotalAmount
|
||||
FROM dbo.trOrderLine
|
||||
GROUP BY OrderHeaderID
|
||||
) l
|
||||
ON l.OrderHeaderID = h.OrderHeaderID
|
||||
|
||||
LEFT JOIN dbo.cdCurrAccDesc ca
|
||||
ON ca.CurrAccCode = h.CurrAccCode
|
||||
AND ca.LangCode = 'TR'
|
||||
|
||||
-- müşteri temsilcisi + piyasa açıklamaları
|
||||
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
|
||||
|
||||
----------------------------------------------------------------
|
||||
-- ORDER PB → 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
|
||||
|
||||
-- 🔐 PIYASA AUTHZ (EXISTS — SAĞLAM YOL)
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM dbo.CustomerAttributesFilter f2
|
||||
WHERE f2.CurrAccCode = h.CurrAccCode
|
||||
AND %s
|
||||
)
|
||||
`, piyasaWhere)
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 🔍 SEARCH FILTER (CASE + TR SAFE)
|
||||
// ----------------------------------------------------
|
||||
if search != "" {
|
||||
baseQuery += `
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM dbo.trOrderHeader h2
|
||||
LEFT JOIN dbo.cdCurrAccDesc ca2
|
||||
ON ca2.CurrAccCode = h2.CurrAccCode
|
||||
AND ca2.LangCode = 'TR'
|
||||
WHERE h2.OrderHeaderID = h.OrderHeaderID
|
||||
AND (
|
||||
LOWER(REPLACE(REPLACE(h2.OrderNumber,'İ','I'),'ı','i'))
|
||||
COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)
|
||||
|
||||
OR LOWER(REPLACE(REPLACE(h2.CurrAccCode,'İ','I'),'ı','i'))
|
||||
COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)
|
||||
|
||||
OR LOWER(REPLACE(REPLACE(ca2.CurrAccDescription,'İ','I'),'ı','i'))
|
||||
COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)
|
||||
|
||||
OR LOWER(REPLACE(REPLACE(h2.Description,'İ','I'),'ı','i'))
|
||||
COLLATE Latin1_General_CI_AI LIKE LOWER(@p1)
|
||||
)
|
||||
)
|
||||
`
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// 📌 ORDER
|
||||
// ----------------------------------------------------
|
||||
baseQuery += `
|
||||
ORDER BY h.CreatedDate DESC
|
||||
`
|
||||
|
||||
// ----------------------------------------------------
|
||||
// ▶ EXECUTE
|
||||
// ----------------------------------------------------
|
||||
if search != "" {
|
||||
searchLike := fmt.Sprintf("%%%s%%", search)
|
||||
return mssql.Query(baseQuery, searchLike)
|
||||
}
|
||||
|
||||
return mssql.Query(baseQuery)
|
||||
}
|
||||
75
svc/queries/orderlist_base.go
Normal file
75
svc/queries/orderlist_base.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package queries
|
||||
|
||||
const OrderListBaseQuery = `
|
||||
SELECT
|
||||
CAST(h.OrderHeaderID AS NVARCHAR(50)) AS OrderHeaderID,
|
||||
ISNULL(h.OrderNumber, '') AS OrderNumber,
|
||||
CONVERT(varchar, h.OrderDate, 23) AS OrderDate,
|
||||
|
||||
ISNULL(h.CurrAccCode, '') AS CurrAccCode,
|
||||
ISNULL(ca.CurrAccDescription, '') AS CurrAccDescription,
|
||||
|
||||
ISNULL(mt.AttributeDescription, '') AS MusteriTemsilcisi,
|
||||
ISNULL(py.AttributeDescription, '') AS Piyasa,
|
||||
|
||||
CONVERT(varchar, h.CreditableConfirmedDate, 23) AS CreditableConfirmedDate,
|
||||
ISNULL(h.DocCurrencyCode, 'TRY') AS DocCurrencyCode,
|
||||
|
||||
ISNULL(l.TotalAmount, 0) AS TotalAmount,
|
||||
|
||||
CASE
|
||||
WHEN h.DocCurrencyCode = 'USD'
|
||||
THEN ISNULL(l.TotalAmount, 0)
|
||||
WHEN h.DocCurrencyCode = 'TRY'
|
||||
THEN ISNULL(l.TotalAmount, 0) / NULLIF(er.Rate, 1)
|
||||
ELSE 0
|
||||
END AS TotalAmountUSD,
|
||||
|
||||
ISNULL(h.IsCreditableConfirmed, 0) AS IsCreditableConfirmed,
|
||||
ISNULL(h.Description, '') AS Description,
|
||||
|
||||
ISNULL(er.Rate, 1) AS ExchangeRateUSD
|
||||
|
||||
FROM dbo.trOrderHeader h
|
||||
|
||||
JOIN (
|
||||
SELECT OrderHeaderID, SUM(Qty1 * Price) AS TotalAmount
|
||||
FROM dbo.trOrderLine
|
||||
GROUP BY OrderHeaderID
|
||||
) l ON l.OrderHeaderID = h.OrderHeaderID
|
||||
|
||||
LEFT JOIN dbo.cdCurrAccDesc ca
|
||||
ON ca.CurrAccCode = h.CurrAccCode AND ca.LangCode = 'TR'
|
||||
|
||||
LEFT JOIN dbo.CustomerAttributes f
|
||||
ON f.CurrAccTypeCode = h.CurrAccTypeCode
|
||||
AND f.CurrAccCode = h.CurrAccCode
|
||||
|
||||
LEFT JOIN dbo.cdCurrAccAttributeDesc mt
|
||||
ON mt.CurrAccTypeCode = f.CurrAccTypeCode
|
||||
AND mt.AttributeTypeCode = 2
|
||||
AND mt.AttributeCode = f.CustomerAtt02
|
||||
AND mt.LangCode = 'TR'
|
||||
|
||||
LEFT JOIN dbo.cdCurrAccAttributeDesc py
|
||||
ON py.CurrAccTypeCode = f.CurrAccTypeCode
|
||||
AND py.AttributeTypeCode = 3
|
||||
AND py.AttributeCode = f.CustomerAtt03
|
||||
AND py.LangCode = 'TR'
|
||||
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1 Rate
|
||||
FROM dbo.AllExchangeRates er
|
||||
WHERE er.CurrencyCode = 'USD'
|
||||
AND er.RelationCurrencyCode = 'TRY'
|
||||
AND er.ExchangeTypeCode = 6
|
||||
AND er.Rate > 0
|
||||
ORDER BY ABS(DATEDIFF(DAY, er.Date, GETDATE()))
|
||||
) er
|
||||
|
||||
WHERE
|
||||
ISNULL(h.IsCancelOrder, 0) = 0
|
||||
AND h.OrderTypeCode = 1
|
||||
AND h.ProcessCode = 'WS'
|
||||
AND h.IsClosed = 0
|
||||
`
|
||||
36
svc/queries/orderpricelistb2b.go
Normal file
36
svc/queries/orderpricelistb2b.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetOrderPriceListB2B → model + currency bazlı ürün fiyatını döndürür (PostgreSQL sürümü)
|
||||
func GetOrderPriceListB2B(db *sql.DB, modelCode string, currency string) (*models.OrderPriceListB2B, error) {
|
||||
query := `
|
||||
SELECT
|
||||
mmitem.code AS ModelCode,
|
||||
sdprc.crn AS CurrencyCode,
|
||||
sdprc.prc AS Price,
|
||||
sdprc.sdprcgrp_id AS PriceGroupID,
|
||||
TO_CHAR(sdprc.zlins_dttm, 'YYYY-MM-DD') AS LastUpdate
|
||||
FROM sdprc
|
||||
LEFT JOIN mmitem ON sdprc.mmitem_id = mmitem.id
|
||||
WHERE mmitem.code = $1
|
||||
AND sdprc.prc IS NOT NULL
|
||||
AND sdprc.prc > 0
|
||||
AND sdprc.crn = $2
|
||||
AND sdprc.sdprcgrp_id = 1
|
||||
ORDER BY sdprc.zlins_dttm DESC
|
||||
LIMIT 1;
|
||||
`
|
||||
|
||||
row := db.QueryRow(query, modelCode, currency)
|
||||
var p models.OrderPriceListB2B
|
||||
err := row.Scan(&p.ModelCode, &p.CurrencyCode, &p.Price, &p.PriceGroupID, &p.LastUpdate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ürün fiyatı bulunamadı: %v", err)
|
||||
}
|
||||
return &p, nil
|
||||
}
|
||||
47
svc/queries/permission_role_dept.go
Normal file
47
svc/queries/permission_role_dept.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package queries
|
||||
|
||||
/* ======================================================
|
||||
ROLE + DEPARTMENT PERMISSIONS
|
||||
====================================================== */
|
||||
|
||||
// GET
|
||||
const GetRoleDepartmentPermissions = `
|
||||
SELECT
|
||||
rdp.module_code,
|
||||
rdp.action,
|
||||
rdp.allowed
|
||||
FROM vw_role_dept_permissions rdp
|
||||
WHERE rdp.role_id = $1
|
||||
AND rdp.department_code = $2
|
||||
ORDER BY rdp.module_code, rdp.action
|
||||
`
|
||||
|
||||
// UPSERT
|
||||
const UpsertRoleDepartmentPermission = `
|
||||
INSERT INTO mk_sys_role_department_permissions
|
||||
(
|
||||
role_id,
|
||||
department_code,
|
||||
module_code,
|
||||
action,
|
||||
allowed
|
||||
)
|
||||
VALUES ($1,$2,$3,$4,$5)
|
||||
|
||||
ON CONFLICT ON CONSTRAINT uq_role_dept_module_action
|
||||
DO UPDATE SET
|
||||
allowed = EXCLUDED.allowed;
|
||||
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 📦 MODULES
|
||||
// ======================================================
|
||||
|
||||
const GetModuleLookup = `
|
||||
SELECT
|
||||
code AS value,
|
||||
name AS label
|
||||
FROM mk_sys_modules
|
||||
ORDER BY id
|
||||
`
|
||||
34
svc/queries/product.go
Normal file
34
svc/queries/product.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
)
|
||||
|
||||
// GetProductList → MSSQL'den ürün listesini döndürür
|
||||
func GetProductList() ([]models.Product, error) {
|
||||
rows, err := db.MssqlDB.Query(`
|
||||
SELECT
|
||||
ProductCode
|
||||
FROM ProductFilterWithDescription('TR')
|
||||
WHERE
|
||||
ProductAtt42 IN ('SERI', 'AKSESUAR')
|
||||
AND IsBlocked = 0
|
||||
AND LEN(ProductCode) = 13 -- 🔹 yalnızca 13 karakterlik kodlar
|
||||
ORDER BY ProductCode;
|
||||
`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var list []models.Product
|
||||
for rows.Next() {
|
||||
var p models.Product
|
||||
if err := rows.Scan(&p.ProductCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list = append(list, p)
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
20
svc/queries/productcolor.go
Normal file
20
svc/queries/productcolor.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package queries
|
||||
|
||||
const GetProductColors = `
|
||||
DECLARE @ProductCode VARCHAR(30) = @p1;
|
||||
|
||||
SELECT DISTINCT
|
||||
p.ProductCode,
|
||||
v.ColorCode,
|
||||
ISNULL(cd.ColorDescription, '') AS ColorDescription
|
||||
FROM ProductFilterWithDescription('TR') AS p
|
||||
INNER JOIN prItemVariant AS v WITH(NOLOCK)
|
||||
ON v.ItemCode = p.ProductCode
|
||||
LEFT JOIN cdColorDesc AS cd WITH(NOLOCK)
|
||||
ON cd.ColorCode = v.ColorCode
|
||||
AND cd.LangCode = 'TR'
|
||||
WHERE
|
||||
p.ProductCode = @ProductCode
|
||||
AND ISNULL(v.ColorCode, '') <> ''
|
||||
ORDER BY v.ColorCode;
|
||||
`
|
||||
66
svc/queries/productcolorsize.go
Normal file
66
svc/queries/productcolorsize.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package queries
|
||||
|
||||
const GetProductColorSizes = `
|
||||
------------------------------------------------------------
|
||||
-- 🧩 GetProductColorSizes
|
||||
-- Kullanım:
|
||||
-- @p1 = ProductCode
|
||||
-- @p2 = ColorCode
|
||||
-- @p3 = ColorCode2
|
||||
-- Açıklama:
|
||||
-- Renk veya ikinci renk boşsa buna göre filtre uygular.
|
||||
-- Renksiz (aksesuar) ürünlerde ColorCode='' kayıtlarını getirir.
|
||||
------------------------------------------------------------
|
||||
|
||||
DECLARE @ProductCode NVARCHAR(30) = @p1;
|
||||
DECLARE @ColorCode NVARCHAR(30) = @p2;
|
||||
DECLARE @ColorCode2 NVARCHAR(30) = @p3;
|
||||
|
||||
-- 🔧 Normalize parametreler (boşlukları kırp ve düzelt)
|
||||
SET @ColorCode = LTRIM(RTRIM(ISNULL(@ColorCode, '')));
|
||||
SET @ColorCode2 = LTRIM(RTRIM(ISNULL(@ColorCode2, '')));
|
||||
|
||||
IF @ColorCode = ''
|
||||
BEGIN
|
||||
SET @ColorCode = ''; -- tamamen renksiz ürün
|
||||
END
|
||||
|
||||
IF @ColorCode2 = ''
|
||||
BEGIN
|
||||
SET @ColorCode2 = NULL; -- 2. renk yok
|
||||
END
|
||||
|
||||
------------------------------------------------------------
|
||||
-- 🔹 Ana sorgu
|
||||
------------------------------------------------------------
|
||||
SELECT
|
||||
Product.ProductCode,
|
||||
ISNULL(prItemVariant.ColorCode, '') AS ColorCode,
|
||||
ISNULL(prItemVariant.ItemDim1Code, '') AS ItemDim1Code,
|
||||
ISNULL(prItemVariant.ItemDim2Code, '') AS ItemDim2Code
|
||||
FROM ProductFilterWithDescription('TR') AS Product
|
||||
INNER JOIN cdItem WITH(NOLOCK)
|
||||
ON cdItem.ItemTypeCode = 1
|
||||
AND cdItem.ItemCode = Product.ProductCode
|
||||
LEFT JOIN prProductLot WITH(NOLOCK)
|
||||
ON prProductLot.ItemTypeCode = cdItem.ItemTypeCode
|
||||
AND prProductLot.ItemCode = cdItem.ItemCode
|
||||
AND prProductLot.IsDefault = 1
|
||||
LEFT JOIN prItemVariant WITH(NOLOCK)
|
||||
ON prItemVariant.ItemCode = Product.ProductCode
|
||||
WHERE
|
||||
ProductAtt42 IN ('SERI','AKSESUAR')
|
||||
AND Product.IsBlocked = 0
|
||||
AND Product.ProductCode = @ProductCode
|
||||
AND (
|
||||
-- 🔸 1. renk doluysa o renk
|
||||
(LTRIM(RTRIM(@ColorCode)) <> '' AND prItemVariant.ColorCode = @ColorCode)
|
||||
-- 🔸 1. renk boşsa renksiz varyantlar
|
||||
OR (LTRIM(RTRIM(@ColorCode)) = '' AND (prItemVariant.ColorCode IS NULL OR LTRIM(RTRIM(prItemVariant.ColorCode)) = ''))
|
||||
-- 🔸 2. renk varsa ek olarak dahil et
|
||||
OR (@ColorCode2 IS NOT NULL AND prItemVariant.ColorCode = @ColorCode2)
|
||||
)
|
||||
ORDER BY
|
||||
prItemVariant.ItemDim1Code,
|
||||
prItemVariant.ItemDim2Code;
|
||||
`
|
||||
36
svc/queries/productdetail.go
Normal file
36
svc/queries/productdetail.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package queries
|
||||
|
||||
const GetProductDetail = `
|
||||
DECLARE @ProductCode VARCHAR(30) = @p1;
|
||||
|
||||
SELECT
|
||||
ProductCode,
|
||||
'' AS BOS7,
|
||||
ProductDescription,
|
||||
Product.ProductAtt42,
|
||||
Product.ProductAtt42Desc AS URUN_ILK_GRUBU,
|
||||
Product.ProductAtt01,
|
||||
Product.ProductAtt01Desc AS URUN_ANA_GRUBU,
|
||||
Product.ProductAtt02,
|
||||
Product.ProductAtt02Desc AS URUN_ALT_GRUBU,
|
||||
Product.ProductAtt41,
|
||||
Product.ProductAtt41Desc AS ICERIK,
|
||||
Product.ProductAtt10,
|
||||
Product.ProductAtt10Desc AS MARKA,
|
||||
Product.ProductAtt11,
|
||||
Product.ProductAtt11Desc AS DROP_,
|
||||
Product.ProductAtt29,
|
||||
Product.ProductAtt29Desc AS KARISIM,
|
||||
Product.ProductAtt45,
|
||||
Product.ProductAtt45Desc AS ASKILI_YAN,
|
||||
Product.ProductAtt44,
|
||||
Product.ProductAtt44Desc AS KATEGORI,
|
||||
Product.ProductAtt38,
|
||||
Product.ProductAtt38Desc AS FIT1,
|
||||
Product.ProductAtt39,
|
||||
Product.ProductAtt39Desc AS FIT2
|
||||
FROM ProductFilterWithDescription('TR') AS Product
|
||||
WHERE ProductAtt42 IN ('SERI','AKSESUAR')
|
||||
AND IsBlocked = 0
|
||||
AND ProductCode = @p1;
|
||||
`
|
||||
18
svc/queries/productsecondcolor.go
Normal file
18
svc/queries/productsecondcolor.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package queries
|
||||
|
||||
const GetProductSecondColors = `
|
||||
SELECT DISTINCT
|
||||
Product.ProductCode,
|
||||
ISNULL(prItemVariant.ColorCode, '') AS ColorCode,
|
||||
ISNULL(prItemVariant.ItemDim2Code, '') AS ItemDim2Code
|
||||
FROM prItemVariant WITH(NOLOCK)
|
||||
INNER JOIN ProductFilterWithDescription('TR') AS Product
|
||||
ON prItemVariant.ItemCode = Product.ProductCode
|
||||
LEFT JOIN cdColorDesc AS ColorDesc WITH(NOLOCK)
|
||||
ON ColorDesc.ColorCode = prItemVariant.ItemDim2Code
|
||||
AND ColorDesc.LangCode = 'TR'
|
||||
WHERE Product.ProductCode = @ProductCode
|
||||
AND prItemVariant.ColorCode = @ColorCode
|
||||
AND ISNULL(prItemVariant.ItemDim2Code, '') <> ''
|
||||
GROUP BY Product.ProductCode, prItemVariant.ItemDim2Code, prItemVariant.ColorCode
|
||||
`
|
||||
172
svc/queries/statement_header.go
Normal file
172
svc/queries/statement_header.go
Normal file
@@ -0,0 +1,172 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Ana tabloyu getiren fonksiyon (Vue header tablosu için)
|
||||
func GetStatements(params models.StatementParams) ([]models.StatementHeader, error) {
|
||||
// AccountCode normalize: "ZLA0127" → "ZLA 0127"
|
||||
if len(params.AccountCode) == 7 && strings.ContainsAny(params.AccountCode, "0123456789") {
|
||||
params.AccountCode = params.AccountCode[:3] + " " + params.AccountCode[3:]
|
||||
}
|
||||
|
||||
// Parislemler []string → '1','2','3'
|
||||
parislemFilter := "''"
|
||||
if len(params.Parislemler) > 0 {
|
||||
quoted := make([]string, len(params.Parislemler))
|
||||
for i, v := range params.Parislemler {
|
||||
quoted[i] = fmt.Sprintf("'%s'", v)
|
||||
}
|
||||
parislemFilter = strings.Join(quoted, ",")
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
;WITH Opening AS (
|
||||
SELECT
|
||||
b.CurrAccCode AS Cari_Kod,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
SUM(c.Debit - c.Credit) AS Devir_Bakiyesi
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON c.CurrAccBookID = b.CurrAccBookID
|
||||
AND c.CurrencyCode = b.DocCurrencyCode
|
||||
WHERE b.CurrAccCode LIKE '%%' + @Carikod + '%%'
|
||||
AND b.DocumentDate < @startdate
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
WHERE f2.CurrAccBookID = b.CurrAccBookID
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
)
|
||||
GROUP BY b.CurrAccCode, b.DocCurrencyCode
|
||||
),
|
||||
Movements AS (
|
||||
SELECT
|
||||
b.CurrAccCode AS Cari_Kod,
|
||||
d.CurrAccDescription AS Cari_Isim,
|
||||
CONVERT(varchar(10), b.DocumentDate, 23) AS Belge_Tarihi,
|
||||
CONVERT(varchar(10), b.DueDate, 23) AS Vade_Tarihi,
|
||||
b.RefNumber AS Belge_No,
|
||||
b.BaseApplicationCode AS Islem_Tipi,
|
||||
b.LineDescription AS Aciklama,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
c.Debit AS Borc,
|
||||
c.Credit AS Alacak,
|
||||
SUM(c.Debit - c.Credit)
|
||||
OVER (PARTITION BY b.CurrAccCode, c.CurrencyCode
|
||||
ORDER BY b.DocumentDate, b.CurrAccBookID) AS Hareket_Bakiyesi,
|
||||
f.ATAtt01 AS Parislemtipi
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON b.CurrAccCode = d.CurrAccCode
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON b.CurrAccBookID = c.CurrAccBookID
|
||||
AND b.DocCurrencyCode = c.CurrencyCode
|
||||
LEFT JOIN CurrAccBookATAttributesFilter f
|
||||
ON b.CurrAccBookID = f.CurrAccBookID
|
||||
WHERE b.CurrAccCode LIKE '%%' + @Carikod + '%%'
|
||||
AND b.DocumentDate BETWEEN @startdate AND @enddate
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
WHERE f2.CurrAccBookID = b.CurrAccBookID
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
)
|
||||
)
|
||||
SELECT
|
||||
m.Cari_Kod,
|
||||
m.Cari_Isim,
|
||||
m.Belge_Tarihi,
|
||||
m.Vade_Tarihi,
|
||||
m.Belge_No,
|
||||
m.Islem_Tipi,
|
||||
m.Aciklama,
|
||||
m.Para_Birimi,
|
||||
m.Borc,
|
||||
m.Alacak,
|
||||
ISNULL(o.Devir_Bakiyesi,0) + m.Hareket_Bakiyesi AS Bakiye,
|
||||
m.Parislemtipi AS Parislemler
|
||||
FROM Movements m
|
||||
LEFT JOIN Opening o
|
||||
ON o.Cari_Kod = m.Cari_Kod
|
||||
AND o.Para_Birimi = m.Para_Birimi
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Devir satırı
|
||||
SELECT
|
||||
@Carikod AS Cari_Kod,
|
||||
MAX(d.CurrAccDescription) AS Cari_Isim,
|
||||
CONVERT(varchar(10), @startdate, 23) AS Belge_Tarihi,
|
||||
CONVERT(varchar(10), @startdate, 23) AS Vade_Tarihi,
|
||||
'Baslangic_devir' AS Belge_No,
|
||||
'Devir' AS Islem_Tipi,
|
||||
'Devir Bakiyesi' AS Aciklama,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
SUM(c.Debit) AS Borc,
|
||||
SUM(c.Credit) AS Alacak,
|
||||
SUM(c.Debit) - SUM(c.Credit) AS Bakiye,
|
||||
(
|
||||
SELECT STRING_AGG(x.ATAtt01, ',')
|
||||
FROM (
|
||||
SELECT DISTINCT f2.ATAtt01
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
INNER JOIN trCurrAccBook bb
|
||||
ON f2.CurrAccBookID = bb.CurrAccBookID
|
||||
WHERE bb.CurrAccCode LIKE '%%' + @Carikod + '%%'
|
||||
AND bb.DocumentDate < @startdate
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
) x
|
||||
) AS Parislemler
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON b.CurrAccCode = d.CurrAccCode
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON b.CurrAccBookID = c.CurrAccBookID
|
||||
AND b.DocCurrencyCode = c.CurrencyCode
|
||||
WHERE b.CurrAccCode LIKE '%%' + @Carikod + '%%'
|
||||
AND b.DocumentDate < @startdate
|
||||
GROUP BY b.DocCurrencyCode
|
||||
|
||||
ORDER BY Para_Birimi, Belge_Tarihi;
|
||||
`, parislemFilter, parislemFilter, parislemFilter)
|
||||
|
||||
rows, err := db.MssqlDB.Query(query,
|
||||
sql.Named("startdate", params.StartDate),
|
||||
sql.Named("enddate", params.EndDate),
|
||||
sql.Named("Carikod", params.AccountCode),
|
||||
sql.Named("LangCode", params.LangCode),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("MSSQL query error: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []models.StatementHeader
|
||||
for rows.Next() {
|
||||
var r models.StatementHeader
|
||||
if err := rows.Scan(
|
||||
&r.CariKod,
|
||||
&r.CariIsim,
|
||||
&r.BelgeTarihi,
|
||||
&r.VadeTarihi,
|
||||
&r.BelgeNo,
|
||||
&r.IslemTipi,
|
||||
&r.Aciklama,
|
||||
&r.ParaBirimi,
|
||||
&r.Borc,
|
||||
&r.Alacak,
|
||||
&r.Bakiye,
|
||||
&r.Parislemler,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, r)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
187
svc/queries/statement_header_pdf.go
Normal file
187
svc/queries/statement_header_pdf.go
Normal file
@@ -0,0 +1,187 @@
|
||||
// queries/statements_header_pdf.go
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// küçük yardımcı: boşlukları temizle, her değeri ayrı tırnakla sar
|
||||
func buildQuotedHList(vals []string) string {
|
||||
var pp []string
|
||||
for _, v := range vals {
|
||||
v = strings.TrimSpace(v)
|
||||
if v != "" {
|
||||
pp = append(pp, fmt.Sprintf("'%s'", v)) // '1','2' gibi
|
||||
}
|
||||
}
|
||||
if len(pp) == 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(pp, ",")
|
||||
}
|
||||
|
||||
/* ============================ HEADER (Ana Tablo) ============================ */
|
||||
|
||||
func GetStatementsHPDF(accountCode, startDate, endDate string, parislemler []string) ([]models.StatementHeader, []string, error) {
|
||||
// Account normalize
|
||||
if len(accountCode) == 7 && strings.ContainsAny(accountCode, "0123456789") {
|
||||
accountCode = accountCode[:3] + " " + accountCode[3:]
|
||||
}
|
||||
|
||||
// IN list parse et
|
||||
inList := buildQuotedHList(parislemler)
|
||||
parislemCond := "''"
|
||||
if inList != "" {
|
||||
parislemCond = inList
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
;WITH Opening AS (
|
||||
SELECT
|
||||
b.CurrAccCode AS Cari_Kod,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
SUM(c.Debit - c.Credit) AS Devir_Bakiyesi
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON c.CurrAccBookID = b.CurrAccBookID
|
||||
AND c.CurrencyCode = b.DocCurrencyCode
|
||||
WHERE b.CurrAccCode LIKE @Carikod
|
||||
AND b.DocumentDate < @StartDate
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
WHERE f2.CurrAccBookID = b.CurrAccBookID
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
)
|
||||
GROUP BY b.CurrAccCode, b.DocCurrencyCode
|
||||
),
|
||||
Movements AS (
|
||||
SELECT
|
||||
b.CurrAccCode AS Cari_Kod,
|
||||
d.CurrAccDescription AS Cari_Isim,
|
||||
CONVERT(varchar(10), b.DocumentDate, 23) AS Belge_Tarihi,
|
||||
CONVERT(varchar(10), b.DueDate, 23) AS Vade_Tarihi,
|
||||
b.RefNumber AS Belge_No,
|
||||
b.BaseApplicationCode AS Islem_Tipi,
|
||||
b.LineDescription AS Aciklama,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
c.Debit AS Borc,
|
||||
c.Credit AS Alacak,
|
||||
SUM(c.Debit - c.Credit)
|
||||
OVER (PARTITION BY b.CurrAccCode, c.CurrencyCode
|
||||
ORDER BY b.DocumentDate, b.CurrAccBookID) AS Hareket_Bakiyesi,
|
||||
f.ATAtt01 AS Parislemler
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON b.CurrAccCode = d.CurrAccCode AND d.LangCode = 'TR'
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON b.CurrAccBookID = c.CurrAccBookID
|
||||
AND b.DocCurrencyCode = c.CurrencyCode
|
||||
LEFT JOIN CurrAccBookATAttributesFilter f
|
||||
ON b.CurrAccBookID = f.CurrAccBookID
|
||||
WHERE b.CurrAccCode LIKE @Carikod
|
||||
AND b.DocumentDate BETWEEN @StartDate AND @EndDate
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
WHERE f2.CurrAccBookID = b.CurrAccBookID
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
)
|
||||
)`, parislemCond, parislemCond)
|
||||
query += fmt.Sprintf(`
|
||||
SELECT
|
||||
m.Cari_Kod,
|
||||
m.Cari_Isim,
|
||||
m.Belge_Tarihi,
|
||||
m.Vade_Tarihi,
|
||||
m.Belge_No,
|
||||
m.Islem_Tipi,
|
||||
m.Aciklama,
|
||||
m.Para_Birimi,
|
||||
m.Borc,
|
||||
m.Alacak,
|
||||
ISNULL(o.Devir_Bakiyesi,0) + m.Hareket_Bakiyesi AS Bakiye,
|
||||
m.Parislemler
|
||||
FROM Movements m
|
||||
LEFT JOIN Opening o
|
||||
ON o.Cari_Kod = m.Cari_Kod
|
||||
AND o.Para_Birimi = m.Para_Birimi
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Devir satırı
|
||||
SELECT
|
||||
@Carikod AS Cari_Kod,
|
||||
MAX(d.CurrAccDescription) AS Cari_Isim,
|
||||
CONVERT(varchar(10), @StartDate, 23) AS Belge_Tarihi,
|
||||
CONVERT(varchar(10), @StartDate, 23) AS Vade_Tarihi,
|
||||
'Baslangic_devir' AS Belge_No,
|
||||
'Devir' AS Islem_Tipi,
|
||||
'Devir Bakiyesi' AS Aciklama,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
SUM(c.Debit) AS Borc,
|
||||
SUM(c.Credit) AS Alacak,
|
||||
SUM(c.Debit) - SUM(c.Credit) AS Bakiye,
|
||||
(
|
||||
SELECT STRING_AGG(x.ATAtt01, ',')
|
||||
FROM (
|
||||
SELECT DISTINCT f2.ATAtt01
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
INNER JOIN trCurrAccBook bb
|
||||
ON f2.CurrAccBookID = bb.CurrAccBookID
|
||||
WHERE bb.CurrAccCode LIKE @Carikod
|
||||
AND bb.DocumentDate < @StartDate
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
) x
|
||||
) AS Parislemler
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON b.CurrAccCode = d.CurrAccCode AND d.LangCode = 'TR'
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON b.CurrAccBookID = c.CurrAccBookID
|
||||
AND b.DocCurrencyCode = c.CurrencyCode
|
||||
WHERE b.CurrAccCode LIKE @Carikod
|
||||
AND b.DocumentDate < @StartDate
|
||||
GROUP BY b.DocCurrencyCode
|
||||
|
||||
ORDER BY Para_Birimi, Belge_Tarihi;`, parislemCond)
|
||||
|
||||
rows, err := db.MssqlDB.Query(query,
|
||||
sql.Named("Carikod", "%"+accountCode+"%"),
|
||||
sql.Named("StartDate", startDate),
|
||||
sql.Named("EndDate", endDate),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("❌ Header sorgu hatası: %v", err)
|
||||
return nil, nil, fmt.Errorf("header sorgu hatası: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var headers []models.StatementHeader
|
||||
var belgeNos []string
|
||||
for rows.Next() {
|
||||
var h models.StatementHeader
|
||||
if err := rows.Scan(
|
||||
&h.CariKod, &h.CariIsim,
|
||||
&h.BelgeTarihi, &h.VadeTarihi,
|
||||
&h.BelgeNo, &h.IslemTipi,
|
||||
&h.Aciklama, &h.ParaBirimi,
|
||||
&h.Borc, &h.Alacak,
|
||||
&h.Bakiye, &h.Parislemler,
|
||||
); err != nil {
|
||||
log.Printf("❌ Header scan hatası: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
headers = append(headers, h)
|
||||
if h.BelgeNo != "" {
|
||||
belgeNos = append(belgeNos, h.BelgeNo)
|
||||
}
|
||||
}
|
||||
log.Printf("✅ Header verileri alındı: %d kayıt, %d belge no", len(headers), len(belgeNos))
|
||||
return headers, belgeNos, nil
|
||||
}
|
||||
121
svc/queries/statements_detail.go
Normal file
121
svc/queries/statements_detail.go
Normal file
@@ -0,0 +1,121 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
/* ============================ DETAIL (ALT TABLO) ============================ */
|
||||
|
||||
func GetStatementDetails(accountCode, startDate, endDate string, parislemler []string) ([]models.StatementDetail, error) {
|
||||
// Parislemler filtresi hazırlanır (ör: 1,2,3)
|
||||
inParislem := ""
|
||||
if len(parislemler) > 0 {
|
||||
pp := make([]string, len(parislemler))
|
||||
for i, v := range parislemler {
|
||||
pp[i] = strings.TrimSpace(v)
|
||||
}
|
||||
inParislem = strings.Join(pp, ",")
|
||||
}
|
||||
query := fmt.Sprintf(`
|
||||
SELECT
|
||||
CONVERT(varchar(10), a.InvoiceDate, 23) AS Belge_Tarihi,
|
||||
a.InvoiceNumber AS Belge_Ref_Numarasi,
|
||||
COALESCE(MAX(AnaGrupDesc.AttributeDescription), '') AS Urun_Ana_Grubu,
|
||||
COALESCE(MAX(AltGrupDesc.AttributeDescription), '') AS Urun_Alt_Grubu,
|
||||
COALESCE(MAX(GarsonDesc.AttributeDescription), '') AS Yetiskin_Garson,
|
||||
COALESCE(MAX(FitDesc.AttributeDescription), '') AS Fit,
|
||||
COALESCE(MAX(KisaKarDesc.AttributeDescription), '') AS Icerik,
|
||||
a.ItemCode AS Urun_Kodu,
|
||||
a.ColorCode AS Urun_Rengi,
|
||||
SUM(a.Qty1) AS Toplam_Adet,
|
||||
SUM(ABS(a.Doc_Price)) AS Toplam_Fiyat,
|
||||
CAST(SUM(a.Qty1 * ABS(a.Doc_Price)) AS numeric(18,2)) AS Toplam_Tutar
|
||||
FROM AllInvoicesWithAttributes a
|
||||
LEFT JOIN prItemAttribute AnaGrup
|
||||
ON a.ItemCode = AnaGrup.ItemCode AND AnaGrup.AttributeTypeCode = 1
|
||||
LEFT JOIN cdItemAttributeDesc AnaGrupDesc
|
||||
ON AnaGrup.AttributeTypeCode = AnaGrupDesc.AttributeTypeCode
|
||||
AND AnaGrup.AttributeCode = AnaGrupDesc.AttributeCode
|
||||
AND AnaGrup.ItemTypeCode = AnaGrupDesc.ItemTypeCode
|
||||
|
||||
LEFT JOIN prItemAttribute AltGrup
|
||||
ON a.ItemCode = AltGrup.ItemCode AND AltGrup.AttributeTypeCode = 2
|
||||
LEFT JOIN cdItemAttributeDesc AltGrupDesc
|
||||
ON AltGrup.AttributeTypeCode = AltGrupDesc.AttributeTypeCode
|
||||
AND AltGrup.AttributeCode = AltGrupDesc.AttributeCode
|
||||
AND AltGrup.ItemTypeCode = AltGrupDesc.ItemTypeCode
|
||||
|
||||
LEFT JOIN prItemAttribute Garson
|
||||
ON a.ItemCode = Garson.ItemCode AND Garson.AttributeTypeCode = 44
|
||||
LEFT JOIN cdItemAttributeDesc GarsonDesc
|
||||
ON Garson.AttributeTypeCode = GarsonDesc.AttributeTypeCode
|
||||
AND Garson.AttributeCode = GarsonDesc.AttributeCode
|
||||
AND Garson.ItemTypeCode = GarsonDesc.ItemTypeCode
|
||||
|
||||
LEFT JOIN prItemAttribute FitTbl
|
||||
ON a.ItemCode = FitTbl.ItemCode AND FitTbl.AttributeTypeCode = 38
|
||||
LEFT JOIN cdItemAttributeDesc FitDesc
|
||||
ON FitTbl.AttributeTypeCode = FitDesc.AttributeTypeCode
|
||||
AND FitTbl.AttributeCode = FitDesc.AttributeCode
|
||||
AND FitTbl.ItemTypeCode = FitDesc.ItemTypeCode
|
||||
|
||||
LEFT JOIN prItemAttribute KisaKar
|
||||
ON a.ItemCode = KisaKar.ItemCode AND KisaKar.AttributeTypeCode = 41
|
||||
LEFT JOIN cdItemAttributeDesc KisaKarDesc
|
||||
ON KisaKar.AttributeTypeCode = KisaKarDesc.AttributeTypeCode
|
||||
AND KisaKar.AttributeCode = KisaKarDesc.AttributeCode
|
||||
AND KisaKar.ItemTypeCode = KisaKarDesc.ItemTypeCode
|
||||
WHERE a.CurrAccCode LIKE @Carikod
|
||||
AND a.InvoiceDate BETWEEN @StartDate AND @EndDate
|
||||
%s
|
||||
GROUP BY a.InvoiceDate, a.InvoiceNumber, a.ItemCode, a.ColorCode
|
||||
ORDER BY Belge_Tarihi, Belge_Ref_Numarasi, Urun_Kodu;`,
|
||||
func() string {
|
||||
if inParislem == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(`AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f
|
||||
WHERE f.CurrAccBookID = a.CurrAccBookID
|
||||
AND f.ATAtt01 IN (%s)
|
||||
)`, inParislem)
|
||||
}(),
|
||||
)
|
||||
rows, err := db.MssqlDB.Query(query,
|
||||
sql.Named("Carikod", "%"+accountCode+"%"),
|
||||
sql.Named("StartDate", startDate),
|
||||
sql.Named("EndDate", endDate),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("detay sorgu hatası: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var results []models.StatementDetail
|
||||
for rows.Next() {
|
||||
var d models.StatementDetail
|
||||
if err := rows.Scan(
|
||||
&d.BelgeTarihi,
|
||||
&d.BelgeRefNumarasi,
|
||||
&d.UrunAnaGrubu,
|
||||
&d.UrunAltGrubu,
|
||||
&d.YetiskinGarson,
|
||||
&d.Fit,
|
||||
&d.Icerik,
|
||||
&d.UrunKodu,
|
||||
&d.UrunRengi,
|
||||
&d.ToplamAdet,
|
||||
&d.ToplamFiyat,
|
||||
&d.ToplamTutar,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
results = append(results, d)
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
305
svc/queries/statements_pdf.go
Normal file
305
svc/queries/statements_pdf.go
Normal file
@@ -0,0 +1,305 @@
|
||||
// queries/statements_pdf.go
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/db"
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// küçük yardımcı: boşlukları temizle, her değeri ayrı tırnakla sar
|
||||
func buildQuotedList(vals []string) string {
|
||||
var pp []string
|
||||
for _, v := range vals {
|
||||
v = strings.TrimSpace(v)
|
||||
if v != "" {
|
||||
pp = append(pp, fmt.Sprintf("'%s'", v)) // '1','2' gibi
|
||||
}
|
||||
}
|
||||
if len(pp) == 0 {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(pp, ",")
|
||||
}
|
||||
|
||||
/* ============================ HEADER (Ana Tablo) ============================ */
|
||||
|
||||
func GetStatementsPDF(accountCode, startDate, endDate string, parislemler []string) ([]models.StatementHeader, []string, error) {
|
||||
// Account normalize
|
||||
if len(accountCode) == 7 && strings.ContainsAny(accountCode, "0123456789") {
|
||||
accountCode = accountCode[:3] + " " + accountCode[3:]
|
||||
}
|
||||
|
||||
// IN list parse et
|
||||
inList := buildQuotedList(parislemler)
|
||||
parislemCond := "''"
|
||||
if inList != "" {
|
||||
parislemCond = inList
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
;WITH Opening AS (
|
||||
SELECT
|
||||
b.CurrAccCode AS Cari_Kod,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
SUM(c.Debit - c.Credit) AS Devir_Bakiyesi
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON c.CurrAccBookID = b.CurrAccBookID
|
||||
AND c.CurrencyCode = b.DocCurrencyCode
|
||||
WHERE b.CurrAccCode LIKE @Carikod
|
||||
AND b.DocumentDate < @StartDate
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
WHERE f2.CurrAccBookID = b.CurrAccBookID
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
)
|
||||
GROUP BY b.CurrAccCode, b.DocCurrencyCode
|
||||
),
|
||||
Movements AS (
|
||||
SELECT
|
||||
b.CurrAccCode AS Cari_Kod,
|
||||
d.CurrAccDescription AS Cari_Isim,
|
||||
CONVERT(varchar(10), b.DocumentDate, 23) AS Belge_Tarihi,
|
||||
CONVERT(varchar(10), b.DueDate, 23) AS Vade_Tarihi,
|
||||
b.RefNumber AS Belge_No,
|
||||
b.BaseApplicationCode AS Islem_Tipi,
|
||||
b.LineDescription AS Aciklama,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
c.Debit AS Borc,
|
||||
c.Credit AS Alacak,
|
||||
SUM(c.Debit - c.Credit)
|
||||
OVER (PARTITION BY b.CurrAccCode, c.CurrencyCode
|
||||
ORDER BY b.DocumentDate, b.CurrAccBookID) AS Hareket_Bakiyesi,
|
||||
f.ATAtt01 AS Parislemler
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON b.CurrAccCode = d.CurrAccCode AND d.LangCode = 'TR'
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON b.CurrAccBookID = c.CurrAccBookID
|
||||
AND b.DocCurrencyCode = c.CurrencyCode
|
||||
LEFT JOIN CurrAccBookATAttributesFilter f
|
||||
ON b.CurrAccBookID = f.CurrAccBookID
|
||||
WHERE b.CurrAccCode LIKE @Carikod
|
||||
AND b.DocumentDate BETWEEN @StartDate AND @EndDate
|
||||
AND EXISTS (
|
||||
SELECT 1
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
WHERE f2.CurrAccBookID = b.CurrAccBookID
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
)
|
||||
)`, parislemCond, parislemCond)
|
||||
query += fmt.Sprintf(`
|
||||
SELECT
|
||||
m.Cari_Kod,
|
||||
m.Cari_Isim,
|
||||
m.Belge_Tarihi,
|
||||
m.Vade_Tarihi,
|
||||
m.Belge_No,
|
||||
m.Islem_Tipi,
|
||||
m.Aciklama,
|
||||
m.Para_Birimi,
|
||||
m.Borc,
|
||||
m.Alacak,
|
||||
ISNULL(o.Devir_Bakiyesi,0) + m.Hareket_Bakiyesi AS Bakiye,
|
||||
m.Parislemler
|
||||
FROM Movements m
|
||||
LEFT JOIN Opening o
|
||||
ON o.Cari_Kod = m.Cari_Kod
|
||||
AND o.Para_Birimi = m.Para_Birimi
|
||||
|
||||
UNION ALL
|
||||
|
||||
-- Devir satırı
|
||||
SELECT
|
||||
@Carikod AS Cari_Kod,
|
||||
MAX(d.CurrAccDescription) AS Cari_Isim,
|
||||
CONVERT(varchar(10), @StartDate, 23) AS Belge_Tarihi,
|
||||
CONVERT(varchar(10), @StartDate, 23) AS Vade_Tarihi,
|
||||
'Baslangic_devir' AS Belge_No,
|
||||
'Devir' AS Islem_Tipi,
|
||||
'Devir Bakiyesi' AS Aciklama,
|
||||
b.DocCurrencyCode AS Para_Birimi,
|
||||
SUM(c.Debit) AS Borc,
|
||||
SUM(c.Credit) AS Alacak,
|
||||
SUM(c.Debit) - SUM(c.Credit) AS Bakiye,
|
||||
(
|
||||
SELECT STRING_AGG(x.ATAtt01, ',')
|
||||
FROM (
|
||||
SELECT DISTINCT f2.ATAtt01
|
||||
FROM CurrAccBookATAttributesFilter f2
|
||||
INNER JOIN trCurrAccBook bb
|
||||
ON f2.CurrAccBookID = bb.CurrAccBookID
|
||||
WHERE bb.CurrAccCode LIKE @Carikod
|
||||
AND bb.DocumentDate < @StartDate
|
||||
AND f2.ATAtt01 IN (%s)
|
||||
) x
|
||||
) AS Parislemler
|
||||
FROM trCurrAccBook b
|
||||
LEFT JOIN cdCurrAccDesc d
|
||||
ON b.CurrAccCode = d.CurrAccCode AND d.LangCode = 'TR'
|
||||
LEFT JOIN trCurrAccBookCurrency c
|
||||
ON b.CurrAccBookID = c.CurrAccBookID
|
||||
AND b.DocCurrencyCode = c.CurrencyCode
|
||||
WHERE b.CurrAccCode LIKE @Carikod
|
||||
AND b.DocumentDate < @StartDate
|
||||
GROUP BY b.DocCurrencyCode
|
||||
|
||||
ORDER BY Para_Birimi, Belge_Tarihi;`, parislemCond)
|
||||
|
||||
rows, err := db.MssqlDB.Query(query,
|
||||
sql.Named("Carikod", "%"+accountCode+"%"),
|
||||
sql.Named("StartDate", startDate),
|
||||
sql.Named("EndDate", endDate),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("❌ Header sorgu hatası: %v", err)
|
||||
return nil, nil, fmt.Errorf("header sorgu hatası: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var headers []models.StatementHeader
|
||||
var belgeNos []string
|
||||
for rows.Next() {
|
||||
var h models.StatementHeader
|
||||
if err := rows.Scan(
|
||||
&h.CariKod, &h.CariIsim,
|
||||
&h.BelgeTarihi, &h.VadeTarihi,
|
||||
&h.BelgeNo, &h.IslemTipi,
|
||||
&h.Aciklama, &h.ParaBirimi,
|
||||
&h.Borc, &h.Alacak,
|
||||
&h.Bakiye, &h.Parislemler,
|
||||
); err != nil {
|
||||
log.Printf("❌ Header scan hatası: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
headers = append(headers, h)
|
||||
if h.BelgeNo != "" {
|
||||
belgeNos = append(belgeNos, h.BelgeNo)
|
||||
}
|
||||
}
|
||||
log.Printf("✅ Header verileri alındı: %d kayıt, %d belge no", len(headers), len(belgeNos))
|
||||
return headers, belgeNos, nil
|
||||
}
|
||||
|
||||
/* ============================ DETAIL (Alt Tablo) ============================ */
|
||||
|
||||
func GetDetailsMapPDF(belgeNos []string, startDate, endDate string) (map[string][]models.StatementDetail, error) {
|
||||
result := make(map[string][]models.StatementDetail)
|
||||
if len(belgeNos) == 0 {
|
||||
log.Println("⚠️ GetDetailsMapPDF: belge listesi boş")
|
||||
return result, nil
|
||||
}
|
||||
|
||||
qs := make([]string, 0, len(belgeNos))
|
||||
for _, no := range belgeNos {
|
||||
safe := strings.ReplaceAll(no, "'", "''")
|
||||
qs = append(qs, fmt.Sprintf("'%s'", safe))
|
||||
}
|
||||
inBelge := strings.Join(qs, ",")
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
;WITH BookMap AS (
|
||||
SELECT b.RefNumber AS InvoiceNumber, b.CurrAccBookID
|
||||
FROM trCurrAccBook b
|
||||
WHERE b.RefNumber IN (%s)
|
||||
)
|
||||
SELECT
|
||||
CONVERT(varchar(10), a.InvoiceDate, 23) AS Belge_Tarihi,
|
||||
a.InvoiceNumber AS Belge_Ref_Numarasi,
|
||||
|
||||
MAX(ISNULL(AnaGrupDesc.AttributeDescription, '')) AS Urun_Ana_Grubu,
|
||||
MAX(ISNULL(AltGrupDesc.AttributeDescription, '')) AS Urun_Alt_Grubu,
|
||||
MAX(ISNULL(GarsonDesc.AttributeDescription, '')) AS Yetiskin_Garson,
|
||||
MAX(ISNULL(FitDesc.AttributeDescription, '')) AS Fit,
|
||||
MAX(ISNULL(KisaKarDesc.AttributeDescription, '')) AS Icerik,
|
||||
|
||||
a.ItemCode, a.ColorCode,
|
||||
SUM(a.Qty1), SUM(ABS(a.Doc_Price)),
|
||||
CAST(SUM(a.Qty1 * ABS(a.Doc_Price)) AS numeric(18,2))
|
||||
|
||||
FROM AllInvoicesWithAttributes a
|
||||
JOIN BookMap bm
|
||||
ON bm.InvoiceNumber = a.InvoiceNumber
|
||||
|
||||
-- Ana Grup
|
||||
LEFT JOIN prItemAttribute AnaGrup
|
||||
ON a.ItemCode = AnaGrup.ItemCode AND AnaGrup.AttributeTypeCode = 1
|
||||
LEFT JOIN cdItemAttributeDesc AnaGrupDesc
|
||||
ON AnaGrup.AttributeTypeCode = AnaGrupDesc.AttributeTypeCode
|
||||
AND AnaGrup.AttributeCode = AnaGrupDesc.AttributeCode
|
||||
AND AnaGrup.ItemTypeCode = AnaGrupDesc.ItemTypeCode
|
||||
|
||||
-- Alt Grup
|
||||
LEFT JOIN prItemAttribute AltGrup
|
||||
ON a.ItemCode = AltGrup.ItemCode AND AltGrup.AttributeTypeCode = 2
|
||||
LEFT JOIN cdItemAttributeDesc AltGrupDesc
|
||||
ON AltGrup.AttributeTypeCode = AltGrupDesc.AttributeTypeCode
|
||||
AND AltGrup.AttributeCode = AltGrupDesc.AttributeCode
|
||||
AND AltGrup.ItemTypeCode = AltGrupDesc.ItemTypeCode
|
||||
|
||||
-- Garson
|
||||
LEFT JOIN prItemAttribute Garson
|
||||
ON a.ItemCode = Garson.ItemCode AND Garson.AttributeTypeCode = 44
|
||||
LEFT JOIN cdItemAttributeDesc GarsonDesc
|
||||
ON Garson.AttributeTypeCode = GarsonDesc.AttributeTypeCode
|
||||
AND Garson.AttributeCode = GarsonDesc.AttributeCode
|
||||
AND Garson.ItemTypeCode = GarsonDesc.ItemTypeCode
|
||||
|
||||
-- Fit
|
||||
LEFT JOIN prItemAttribute FitTbl
|
||||
ON a.ItemCode = FitTbl.ItemCode AND FitTbl.AttributeTypeCode = 38
|
||||
LEFT JOIN cdItemAttributeDesc FitDesc
|
||||
ON FitTbl.AttributeTypeCode = FitDesc.AttributeTypeCode
|
||||
AND FitTbl.AttributeCode = FitDesc.AttributeCode
|
||||
AND FitTbl.ItemTypeCode = FitDesc.ItemTypeCode
|
||||
|
||||
-- Kısa Karışım
|
||||
LEFT JOIN prItemAttribute KisaKar
|
||||
ON a.ItemCode = KisaKar.ItemCode AND KisaKar.AttributeTypeCode = 41
|
||||
LEFT JOIN cdItemAttributeDesc KisaKarDesc
|
||||
ON KisaKar.AttributeTypeCode = KisaKarDesc.AttributeTypeCode
|
||||
AND KisaKar.AttributeCode = KisaKarDesc.AttributeCode
|
||||
AND KisaKar.ItemTypeCode = KisaKarDesc.ItemTypeCode
|
||||
|
||||
WHERE a.InvoiceDate BETWEEN @StartDate AND @EndDate
|
||||
GROUP BY a.InvoiceDate, a.InvoiceNumber, a.ItemCode, a.ColorCode
|
||||
ORDER BY a.InvoiceNumber, a.ItemCode, a.ColorCode;`, inBelge)
|
||||
rows, err := db.MssqlDB.Query(query,
|
||||
sql.Named("StartDate", startDate),
|
||||
sql.Named("EndDate", endDate),
|
||||
)
|
||||
if err != nil {
|
||||
log.Printf("❌ Detay sorgu hatası: %v", err)
|
||||
return nil, fmt.Errorf("detay sorgu hatası: %v", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
for rows.Next() {
|
||||
var d models.StatementDetail
|
||||
if err := rows.Scan(
|
||||
&d.BelgeTarihi,
|
||||
&d.BelgeRefNumarasi,
|
||||
&d.UrunAnaGrubu,
|
||||
&d.UrunAltGrubu,
|
||||
&d.YetiskinGarson,
|
||||
&d.Fit,
|
||||
&d.Icerik,
|
||||
&d.UrunKodu,
|
||||
&d.UrunRengi,
|
||||
&d.ToplamAdet,
|
||||
&d.ToplamFiyat,
|
||||
&d.ToplamTutar,
|
||||
); err != nil {
|
||||
log.Printf("❌ Detay scan hatası: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
result[d.BelgeRefNumarasi] = append(result[d.BelgeRefNumarasi], d)
|
||||
}
|
||||
log.Printf("✅ Detay verileri alındı: %d belge için detay var", len(result))
|
||||
return result, nil
|
||||
}
|
||||
35
svc/queries/todaycurrencyv3.go
Normal file
35
svc/queries/todaycurrencyv3.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetTodayCurrencyV3 tek bir döviz türü için güncel kuru döndürür (MSSQL sürümü)
|
||||
func GetTodayCurrencyV3(db *sql.DB, currencyCode string) (*models.TodayCurrencyV3, error) {
|
||||
query := `
|
||||
SELECT TOP 1
|
||||
CurrencyCode,
|
||||
RelationCurrencyCode,
|
||||
ExchangeTypeCode,
|
||||
Rate,
|
||||
CONVERT(varchar, Date, 23) AS Date
|
||||
FROM AllExchangeRates
|
||||
WHERE CurrencyCode = @p1
|
||||
AND RelationCurrencyCode = 'TRY'
|
||||
AND ExchangeTypeCode = 6
|
||||
AND Rate > 0
|
||||
ORDER BY ABS(DATEDIFF(DAY, Date, GETDATE())) ASC
|
||||
`
|
||||
|
||||
row := db.QueryRow(query, currencyCode)
|
||||
|
||||
var c models.TodayCurrencyV3
|
||||
err := row.Scan(&c.CurrencyCode, &c.RelationCurrencyCode, &c.ExchangeTypeCode, &c.Rate, &c.Date)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("kur bilgisi alınamadı: %v", err)
|
||||
}
|
||||
|
||||
return &c, nil
|
||||
}
|
||||
296
svc/queries/user_detail.go
Normal file
296
svc/queries/user_detail.go
Normal file
@@ -0,0 +1,296 @@
|
||||
package queries
|
||||
|
||||
// ======================================================
|
||||
// 👤 USER HEADER
|
||||
// ======================================================
|
||||
// ======================================================
|
||||
// 👤 USER HEADER (mk_dfusr)
|
||||
// ======================================================
|
||||
const GetUserHeader = `
|
||||
SELECT
|
||||
id,
|
||||
username,
|
||||
is_active,
|
||||
COALESCE(full_name,'') AS full_name,
|
||||
COALESCE(email,'') AS email,
|
||||
COALESCE(mobile,'') AS mobile,
|
||||
COALESCE(address,'') AS address,
|
||||
|
||||
CASE
|
||||
WHEN password_hash IS NOT NULL AND password_hash <> '' THEN true
|
||||
ELSE false
|
||||
END AS has_password
|
||||
|
||||
FROM mk_dfusr
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🔐 ROLES
|
||||
// ======================================================
|
||||
const GetUserRoles = `
|
||||
SELECT r.code
|
||||
FROM dfrole_usr ur
|
||||
JOIN dfrole r ON r.id = ur.dfrole_id
|
||||
WHERE ur.dfusr_id = $1
|
||||
ORDER BY r.code
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🏢 DEPARTMENTS
|
||||
// ======================================================
|
||||
const GetUserDepartments = `
|
||||
SELECT d.code, d.title
|
||||
FROM dfusr_dprt ud
|
||||
JOIN mk_dprt d ON d.id = ud.dprt_id
|
||||
WHERE ud.dfusr_id = $1
|
||||
AND ud.is_active = true
|
||||
ORDER BY d.code
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🌍 PIYASALAR
|
||||
// ======================================================
|
||||
const GetUserPiyasalar = `
|
||||
SELECT p.code, p.title
|
||||
FROM dfusr_piyasa up
|
||||
JOIN mk_sales_piy p ON p.code = up.piyasa_code
|
||||
WHERE up.dfusr_id = $1
|
||||
AND up.is_allowed = true
|
||||
ORDER BY p.code
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🧾 NEBIM USERS
|
||||
// ======================================================
|
||||
const GetUserNebim = `
|
||||
SELECT n.username, n.user_group_code
|
||||
FROM dfusr_nebim_user un
|
||||
JOIN mk_nebim_user n ON n.id = un.mk_nebim_user_id
|
||||
WHERE un.dfusr_id = $1
|
||||
ORDER BY n.username
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// ✍️ UPDATE USER HEADER (SAFE)
|
||||
// ======================================================
|
||||
// ======================================================
|
||||
// ✍️ UPDATE USER HEADER (mk_dfusr)
|
||||
// ======================================================
|
||||
const UpdateUserHeader = `
|
||||
UPDATE mk_dfusr
|
||||
SET
|
||||
username = $2,
|
||||
is_active = $3,
|
||||
full_name = $4,
|
||||
email = $5,
|
||||
mobile = $6,
|
||||
address = NULLIF($7, ''),
|
||||
updated_at = NOW()
|
||||
WHERE id = $1
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// ➕ INSERT RELATIONS
|
||||
// ======================================================
|
||||
|
||||
// ---------- ROLE
|
||||
const InsertUserRole = `
|
||||
INSERT INTO dfrole_usr (dfusr_id, dfrole_id)
|
||||
SELECT $1, r.id
|
||||
FROM dfrole r
|
||||
WHERE r.code = $2
|
||||
ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
// ---------- DEPARTMENT
|
||||
const InsertUserDepartment = `
|
||||
INSERT INTO dfusr_dprt (dfusr_id, dprt_id, is_active)
|
||||
SELECT $1, d.id, true
|
||||
FROM mk_dprt d
|
||||
WHERE d.code = $2
|
||||
ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
// ---------- PIYASA
|
||||
const InsertUserPiyasa = `
|
||||
INSERT INTO dfusr_piyasa (dfusr_id, piyasa_code, is_allowed)
|
||||
VALUES ($1, $2, true)
|
||||
ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
// ---------- NEBIM USER
|
||||
const InsertUserNebim = `
|
||||
INSERT INTO dfusr_nebim_user (dfusr_id, mk_nebim_user_id)
|
||||
SELECT $1, n.id
|
||||
FROM mk_nebim_user n
|
||||
WHERE n.username = $2
|
||||
ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 📚 LOOKUPS
|
||||
// ======================================================
|
||||
const GetRoleLookup = `
|
||||
SELECT code AS value, code AS label
|
||||
FROM dfrole
|
||||
ORDER BY code
|
||||
`
|
||||
|
||||
const GetDepartmentLookup = `
|
||||
SELECT code AS value, title AS label
|
||||
FROM mk_dprt
|
||||
WHERE is_active = true
|
||||
ORDER BY code
|
||||
`
|
||||
|
||||
const GetPiyasaLookup = `
|
||||
SELECT code AS value, title AS label
|
||||
FROM mk_sales_piy
|
||||
WHERE is_active = true
|
||||
ORDER BY code
|
||||
`
|
||||
|
||||
const GetNebimUserLookup = `
|
||||
SELECT
|
||||
username AS value,
|
||||
username || ' (' || user_group_code || ')' AS label,
|
||||
user_group_code
|
||||
FROM mk_nebim_user
|
||||
WHERE is_active = true
|
||||
ORDER BY username
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 📦 ORDER — NEW NUMBER
|
||||
// ======================================================
|
||||
|
||||
const GetNextOrderNumber = `
|
||||
SELECT
|
||||
COALESCE(MAX(order_number), 0) + 1
|
||||
FROM orders
|
||||
WHERE order_year = EXTRACT(YEAR FROM NOW())
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🔐 ROLES (ADMIN LIST)
|
||||
// ======================================================
|
||||
|
||||
const GetRoles = `
|
||||
SELECT
|
||||
id,
|
||||
code,
|
||||
title
|
||||
FROM dfrole
|
||||
ORDER BY code
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🏢 DEPARTMENTS (ADMIN LIST)
|
||||
// ======================================================
|
||||
|
||||
const GetDepartments = `
|
||||
SELECT
|
||||
id,
|
||||
code,
|
||||
title
|
||||
FROM mk_dprt
|
||||
WHERE is_active = true
|
||||
ORDER BY code
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🌍 PIYASALAR (ADMIN LIST)
|
||||
// ======================================================
|
||||
|
||||
const GetPiyasalar = `
|
||||
SELECT
|
||||
code,
|
||||
title
|
||||
FROM mk_sales_piy
|
||||
WHERE is_active = true
|
||||
ORDER BY code
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🔗 ROLE → DEPARTMENT
|
||||
// ======================================================
|
||||
|
||||
const DeleteRoleDepartments = `
|
||||
DELETE FROM dfrole_dprt
|
||||
WHERE dfrole_id = $1
|
||||
`
|
||||
|
||||
const InsertRoleDepartment = `
|
||||
INSERT INTO dfrole_dprt (
|
||||
dfrole_id,
|
||||
dprt_id,
|
||||
is_allowed
|
||||
)
|
||||
SELECT
|
||||
$1,
|
||||
d.id,
|
||||
true
|
||||
FROM mk_dprt d
|
||||
WHERE d.code = $2
|
||||
ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🔗 ROLE → PIYASA
|
||||
// ======================================================
|
||||
|
||||
const DeleteRolePiyasalar = `
|
||||
DELETE FROM dfrole_piyasa
|
||||
WHERE dfrole_id = $1
|
||||
`
|
||||
|
||||
const InsertRolePiyasa = `
|
||||
INSERT INTO dfrole_piyasa (
|
||||
dfrole_id,
|
||||
piyasa_code,
|
||||
is_allowed
|
||||
)
|
||||
VALUES ($1,$2,true)
|
||||
ON CONFLICT DO NOTHING
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 👤 USER → ROLE
|
||||
// ======================================================
|
||||
|
||||
const DeleteUserRoles = `
|
||||
DELETE FROM dfrole_usr
|
||||
WHERE dfusr_id = $1
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🏢 DEPARTMENTS (ADMIN PERM PAGE SELECT) -> { code, title }
|
||||
// ======================================================
|
||||
const GetDepartmentsForPermissionSelect = `
|
||||
SELECT
|
||||
code AS id,
|
||||
title AS title
|
||||
FROM mk_dprt
|
||||
WHERE is_active = true
|
||||
ORDER BY title
|
||||
`
|
||||
|
||||
// ======================================================
|
||||
// 🔐 ROLES (ADMIN PERM PAGE SELECT) -> { id, title }
|
||||
// ======================================================
|
||||
const GetRolesForPermissionSelect = `
|
||||
SELECT
|
||||
id::text AS id,
|
||||
COALESCE(NULLIF(title,''), code) AS title
|
||||
FROM dfrole
|
||||
ORDER BY COALESCE(NULLIF(title,''), code)
|
||||
`
|
||||
const GetUserLookupForPermission = `
|
||||
SELECT
|
||||
id::text AS id,
|
||||
username || ' - ' || full_name AS title
|
||||
FROM mk_dfusr
|
||||
WHERE is_active = true
|
||||
ORDER BY username
|
||||
`
|
||||
106
svc/queries/user_list.go
Normal file
106
svc/queries/user_list.go
Normal file
@@ -0,0 +1,106 @@
|
||||
package queries
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// ========================================================
|
||||
// 📌 GetUserList — POSTGRES FINAL (mk_dfusr)
|
||||
// ========================================================
|
||||
func GetUserList(db *sql.DB, search string) (*sql.Rows, error) {
|
||||
|
||||
baseQuery := `
|
||||
SELECT
|
||||
u.id AS id, -- 1
|
||||
u.username AS code, -- 2 (frontend uyumu için alias)
|
||||
u.is_active AS is_active, -- 3
|
||||
|
||||
-- Nebim eşleşmesi (opsiyonel)
|
||||
n.username AS nebim_username, -- 4
|
||||
n.user_group_code AS user_group_code, -- 5
|
||||
|
||||
-- Roller
|
||||
COALESCE(string_agg(DISTINCT r.code, ', '), '') AS role_names, -- 6
|
||||
|
||||
-- Departmanlar
|
||||
COALESCE(string_agg(DISTINCT d.title, ', '), '') AS department_names, -- 7
|
||||
|
||||
-- Piyasalar (ÇOKLU)
|
||||
COALESCE(string_agg(DISTINCT p.title, ', '), '') AS piyasa_names -- 8
|
||||
|
||||
FROM mk_dfusr u
|
||||
|
||||
-- ==========================
|
||||
-- 🔗 Nebim eşleşmesi
|
||||
-- ==========================
|
||||
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
|
||||
|
||||
-- ==========================
|
||||
-- 🔐 Roller
|
||||
-- ==========================
|
||||
LEFT JOIN dfrole_usr uru
|
||||
ON uru.dfusr_id = u.id
|
||||
|
||||
LEFT JOIN dfrole r
|
||||
ON r.id = uru.dfrole_id
|
||||
|
||||
-- ==========================
|
||||
-- 🏢 Departmanlar
|
||||
-- ==========================
|
||||
LEFT JOIN dfusr_dprt ud
|
||||
ON ud.dfusr_id = u.id
|
||||
|
||||
LEFT JOIN mk_dprt d
|
||||
ON d.id = ud.dprt_id
|
||||
|
||||
-- ==========================
|
||||
-- 🌍 Piyasalar
|
||||
-- ==========================
|
||||
LEFT JOIN dfusr_piyasa up
|
||||
ON up.dfusr_id = u.id
|
||||
|
||||
LEFT JOIN mk_sales_piy p
|
||||
ON p.code = up.piyasa_code
|
||||
|
||||
WHERE 1 = 1
|
||||
`
|
||||
|
||||
// 🔍 SEARCH FILTER
|
||||
if search != "" {
|
||||
baseQuery += `
|
||||
AND (
|
||||
u.username ILIKE $1
|
||||
OR u.email ILIKE $1
|
||||
OR n.username ILIKE $1
|
||||
OR r.code ILIKE $1
|
||||
OR d.title ILIKE $1
|
||||
OR p.title ILIKE $1
|
||||
)
|
||||
`
|
||||
}
|
||||
|
||||
// 📌 GROUP + ORDER
|
||||
baseQuery += `
|
||||
GROUP BY
|
||||
u.id,
|
||||
u.username,
|
||||
u.is_active,
|
||||
n.username,
|
||||
n.user_group_code
|
||||
|
||||
ORDER BY u.username
|
||||
`
|
||||
|
||||
// ▶ EXECUTE QUERY
|
||||
if search != "" {
|
||||
searchLike := fmt.Sprintf("%%%s%%", search)
|
||||
return db.Query(baseQuery, searchLike)
|
||||
}
|
||||
|
||||
return db.Query(baseQuery)
|
||||
}
|
||||
41
svc/queries/user_password.go
Normal file
41
svc/queries/user_password.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package queries
|
||||
|
||||
// ======================================================
|
||||
// 🔐 UPDATE USER PASSWORD (RESET / CHANGE)
|
||||
// ======================================================
|
||||
const UpdateUserPassword = `
|
||||
UPDATE dfusr
|
||||
SET
|
||||
upass = $2,
|
||||
last_updated_date = NOW()
|
||||
WHERE id = $1
|
||||
AND is_active = true
|
||||
`
|
||||
|
||||
// Reset token yaz
|
||||
const SetUserResetToken = `
|
||||
UPDATE dfusr
|
||||
SET
|
||||
fpass = $2,
|
||||
fpass_id = NOW()
|
||||
WHERE id = $1
|
||||
AND is_active = true
|
||||
`
|
||||
|
||||
// Reset token kontrol
|
||||
const CheckUserResetToken = `
|
||||
SELECT id
|
||||
FROM dfusr
|
||||
WHERE
|
||||
fpass = $1
|
||||
AND is_active = true
|
||||
`
|
||||
|
||||
// Reset sonrası temizle
|
||||
const ClearUserResetToken = `
|
||||
UPDATE dfusr
|
||||
SET
|
||||
fpass = NULL,
|
||||
fpass_id = NULL
|
||||
WHERE id = $1
|
||||
`
|
||||
Reference in New Issue
Block a user