Merge remote-tracking branch 'origin/master'
This commit is contained in:
344
svc/queries/order_bulk_close.go
Normal file
344
svc/queries/order_bulk_close.go
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/auth"
|
||||||
|
"bssapp-backend/internal/authz"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resolvePiyasaWhere(ctx context.Context, pg *sql.DB) (string, error) {
|
||||||
|
claims, ok := auth.GetClaimsFromContext(ctx)
|
||||||
|
if !ok || claims == nil {
|
||||||
|
return "", fmt.Errorf("unauthorized: claims not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if claims.IsAdmin() {
|
||||||
|
return "1=1", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
codes, err := authz.GetUserPiyasaCodes(pg, int(claims.ID))
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("piyasa codes load error: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(codes) == 0 {
|
||||||
|
return "1=0", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return authz.BuildINClause("UPPER(f2.CustomerAtt01)", codes), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetOrderListCloseReady(
|
||||||
|
ctx context.Context,
|
||||||
|
mssql *sql.DB,
|
||||||
|
pg *sql.DB,
|
||||||
|
search string,
|
||||||
|
) (*sql.Rows, error) {
|
||||||
|
|
||||||
|
piyasaWhere, err := resolvePiyasaWhere(ctx, pg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
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(l.PackedAmount,0) AS PackedAmount,
|
||||||
|
|
||||||
|
CASE
|
||||||
|
WHEN h.DocCurrencyCode = 'USD'
|
||||||
|
THEN ISNULL(l.PackedAmount,0)
|
||||||
|
WHEN h.DocCurrencyCode = 'TRY'
|
||||||
|
AND usd.Rate > 0
|
||||||
|
THEN ISNULL(l.PackedTRY,0) / usd.Rate
|
||||||
|
WHEN cur.Rate > 0
|
||||||
|
AND usd.Rate > 0
|
||||||
|
THEN (ISNULL(l.PackedAmount,0) * cur.Rate) / usd.Rate
|
||||||
|
ELSE 0
|
||||||
|
END AS PackedUSD,
|
||||||
|
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(l.TotalAmount,0) > 0
|
||||||
|
THEN (ISNULL(l.PackedAmount,0) * 100.0) / NULLIF(l.TotalAmount,0)
|
||||||
|
ELSE 0
|
||||||
|
END AS PackedRatePct,
|
||||||
|
|
||||||
|
ISNULL(h.IsCreditableConfirmed,0) AS IsCreditableConfirmed,
|
||||||
|
ISNULL(h.Description,'') AS Description,
|
||||||
|
|
||||||
|
usd.Rate AS ExchangeRateUSD
|
||||||
|
|
||||||
|
FROM dbo.trOrderHeader h
|
||||||
|
|
||||||
|
JOIN (
|
||||||
|
SELECT
|
||||||
|
l.OrderHeaderID,
|
||||||
|
SUM(ISNULL(c.NetAmount,0)) AS TotalAmount,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(c.CurrencyCode,'') = 'TRY'
|
||||||
|
THEN ISNULL(c.NetAmount,0)
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) AS TotalTRY,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(l.IsClosed,0) = 1
|
||||||
|
THEN ISNULL(c.NetAmount,0)
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) AS PackedAmount,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(l.IsClosed,0) = 1
|
||||||
|
AND ISNULL(c.CurrencyCode,'') = 'TRY'
|
||||||
|
THEN ISNULL(c.NetAmount,0)
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) AS PackedTRY
|
||||||
|
FROM dbo.trOrderLine l
|
||||||
|
JOIN dbo.trOrderHeader h2
|
||||||
|
ON h2.OrderHeaderID = l.OrderHeaderID
|
||||||
|
LEFT JOIN dbo.trOrderLineCurrency c
|
||||||
|
ON c.OrderLineID = l.OrderLineID
|
||||||
|
AND c.CurrencyCode = ISNULL(h2.DocCurrencyCode,'TRY')
|
||||||
|
GROUP BY l.OrderHeaderID
|
||||||
|
) l
|
||||||
|
ON l.OrderHeaderID = h.OrderHeaderID
|
||||||
|
|
||||||
|
LEFT JOIN dbo.cdCurrAccDesc ca
|
||||||
|
ON ca.CurrAccCode = h.CurrAccCode
|
||||||
|
AND ca.LangCode = 'TR'
|
||||||
|
|
||||||
|
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'
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
AND ISNULL(l.TotalAmount,0) > 0
|
||||||
|
AND (ISNULL(l.PackedAmount,0) * 100.0) / NULLIF(l.TotalAmount,0) >= 100
|
||||||
|
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.CustomerAttributesFilter f2
|
||||||
|
WHERE f2.CurrAccCode = h.CurrAccCode
|
||||||
|
AND %s
|
||||||
|
)
|
||||||
|
`, piyasaWhere)
|
||||||
|
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
baseQuery += `
|
||||||
|
ORDER BY h.CreatedDate DESC
|
||||||
|
`
|
||||||
|
|
||||||
|
if search != "" {
|
||||||
|
searchLike := fmt.Sprintf("%%%s%%", search)
|
||||||
|
return mssql.Query(baseQuery, searchLike)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mssql.Query(baseQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BulkCloseOrders(
|
||||||
|
ctx context.Context,
|
||||||
|
mssql *sql.DB,
|
||||||
|
pg *sql.DB,
|
||||||
|
orderNumbers []string,
|
||||||
|
updatedBy string,
|
||||||
|
) (int64, error) {
|
||||||
|
|
||||||
|
piyasaWhere, err := resolvePiyasaWhere(ctx, pg)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
clean := make([]string, 0, len(orderNumbers))
|
||||||
|
seen := make(map[string]struct{})
|
||||||
|
|
||||||
|
for _, n := range orderNumbers {
|
||||||
|
n = strings.TrimSpace(n)
|
||||||
|
if n == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := strings.ToUpper(n)
|
||||||
|
if _, ok := seen[key]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
seen[key] = struct{}{}
|
||||||
|
clean = append(clean, n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(clean) == 0 {
|
||||||
|
return 0, fmt.Errorf("order_numbers is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(updatedBy) == "" {
|
||||||
|
updatedBy = "SYSTEM"
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholders := make([]string, len(clean))
|
||||||
|
args := make([]any, 0, len(clean)+1)
|
||||||
|
args = append(args, updatedBy) // @p1
|
||||||
|
|
||||||
|
for i, no := range clean {
|
||||||
|
placeholders[i] = fmt.Sprintf("@p%d", i+2)
|
||||||
|
args = append(args, no)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx, err := mssql.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("begin tx failed: %w", err)
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
q := fmt.Sprintf(`
|
||||||
|
UPDATE h
|
||||||
|
SET
|
||||||
|
h.IsClosed = 1,
|
||||||
|
h.LastUpdatedDate = GETDATE(),
|
||||||
|
h.LastUpdatedUserName = @p1
|
||||||
|
FROM dbo.trOrderHeader h
|
||||||
|
WHERE
|
||||||
|
h.IsClosed = 0
|
||||||
|
AND h.OrderNumber IN (%s)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.CustomerAttributesFilter f2
|
||||||
|
WHERE f2.CurrAccCode = h.CurrAccCode
|
||||||
|
AND %s
|
||||||
|
)
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
l.OrderHeaderID,
|
||||||
|
SUM(ISNULL(c.NetAmount,0)) AS TotalAmount,
|
||||||
|
SUM(
|
||||||
|
CASE
|
||||||
|
WHEN ISNULL(l.IsClosed,0) = 1
|
||||||
|
THEN ISNULL(c.NetAmount,0)
|
||||||
|
ELSE 0
|
||||||
|
END
|
||||||
|
) AS PackedAmount
|
||||||
|
FROM dbo.trOrderLine l
|
||||||
|
JOIN dbo.trOrderHeader h2
|
||||||
|
ON h2.OrderHeaderID = l.OrderHeaderID
|
||||||
|
LEFT JOIN dbo.trOrderLineCurrency c
|
||||||
|
ON c.OrderLineID = l.OrderLineID
|
||||||
|
AND c.CurrencyCode = ISNULL(h2.DocCurrencyCode,'TRY')
|
||||||
|
GROUP BY l.OrderHeaderID
|
||||||
|
) t
|
||||||
|
WHERE t.OrderHeaderID = h.OrderHeaderID
|
||||||
|
AND t.TotalAmount > 0
|
||||||
|
AND (t.PackedAmount * 100.0) / NULLIF(t.TotalAmount,0) >= 100
|
||||||
|
);
|
||||||
|
`, strings.Join(placeholders, ","), piyasaWhere)
|
||||||
|
|
||||||
|
res, err := tx.ExecContext(ctx, q, args...)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("bulk close update failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := res.RowsAffected()
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("rows affected read failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return 0, fmt.Errorf("commit failed: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected, nil
|
||||||
|
}
|
||||||
115
svc/routes/order_bulk_close.go
Normal file
115
svc/routes/order_bulk_close.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/auth"
|
||||||
|
"bssapp-backend/db"
|
||||||
|
"bssapp-backend/models"
|
||||||
|
"bssapp-backend/queries"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type bulkCloseOrdersRequest struct {
|
||||||
|
OrderNumbers []string `json:"order_numbers"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrderCloseReadyListRoute(mssqlDB *sql.DB) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
raw := r.URL.Query().Get("search")
|
||||||
|
search := strings.TrimSpace(raw)
|
||||||
|
|
||||||
|
rows, err := queries.GetOrderListCloseReady(
|
||||||
|
r.Context(),
|
||||||
|
mssqlDB,
|
||||||
|
db.PgDB,
|
||||||
|
search,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ close-ready list query error: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := make([]models.OrderList, 0, 100)
|
||||||
|
for rows.Next() {
|
||||||
|
var o models.OrderList
|
||||||
|
if err := rows.Scan(
|
||||||
|
&o.OrderHeaderID,
|
||||||
|
&o.OrderNumber,
|
||||||
|
&o.OrderDate,
|
||||||
|
&o.CurrAccCode,
|
||||||
|
&o.CurrAccDescription,
|
||||||
|
&o.MusteriTemsilcisi,
|
||||||
|
&o.Piyasa,
|
||||||
|
&o.CreditableConfirmedDate,
|
||||||
|
&o.DocCurrencyCode,
|
||||||
|
&o.TotalAmount,
|
||||||
|
&o.TotalAmountUSD,
|
||||||
|
&o.PackedAmount,
|
||||||
|
&o.PackedUSD,
|
||||||
|
&o.PackedRatePct,
|
||||||
|
&o.IsCreditableConfirmed,
|
||||||
|
&o.Description,
|
||||||
|
&o.ExchangeRateUSD,
|
||||||
|
); err != nil {
|
||||||
|
log.Printf("⚠️ close-ready scan error: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("⚠️ close-ready rows err: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(w).Encode(list)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func OrderBulkCloseRoute(mssqlDB *sql.DB) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
var req bulkCloseOrdersRequest
|
||||||
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
|
http.Error(w, "Geçersiz JSON", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(req.OrderNumbers) == 0 {
|
||||||
|
http.Error(w, "order_numbers zorunludur", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
updatedBy := "SYSTEM"
|
||||||
|
if claims, ok := auth.GetClaimsFromContext(r.Context()); ok && claims != nil {
|
||||||
|
if u := strings.TrimSpace(claims.Username); u != "" {
|
||||||
|
updatedBy = u
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := queries.BulkCloseOrders(
|
||||||
|
r.Context(),
|
||||||
|
mssqlDB,
|
||||||
|
db.PgDB,
|
||||||
|
req.OrderNumbers,
|
||||||
|
updatedBy,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ bulk close failed: %v", err)
|
||||||
|
http.Error(w, "Toplu kapatma sırasında hata oluştu", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||||
|
"success": true,
|
||||||
|
"affected": affected,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user