Merge remote-tracking branch 'origin/master'
This commit is contained in:
19
svc/models/orderproductionitem.go
Normal file
19
svc/models/orderproductionitem.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// 📌 OrderProductionItem — U ile başlayan ürün satırı
|
||||||
|
// ========================================================
|
||||||
|
type OrderProductionItem struct {
|
||||||
|
OrderHeaderID string `json:"OrderHeaderID"`
|
||||||
|
OrderLineID string `json:"OrderLineID"`
|
||||||
|
|
||||||
|
OldItemCode string `json:"OldItemCode"`
|
||||||
|
OldColor string `json:"OldColor"`
|
||||||
|
OldDim2 string `json:"OldDim2"`
|
||||||
|
OldDesc string `json:"OldDesc"`
|
||||||
|
|
||||||
|
NewItemCode string `json:"NewItemCode"`
|
||||||
|
NewColor string `json:"NewColor"`
|
||||||
|
NewDim2 string `json:"NewDim2"`
|
||||||
|
NewDesc string `json:"NewDesc"`
|
||||||
|
}
|
||||||
31
svc/queries/orderproduction_items.go
Normal file
31
svc/queries/orderproduction_items.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// 📌 GetOrderProductionItems — OrderHeaderID için U ürünleri
|
||||||
|
// ========================================================
|
||||||
|
func GetOrderProductionItems(mssql *sql.DB, orderHeaderID string) (*sql.Rows, error) {
|
||||||
|
return mssql.Query(`
|
||||||
|
SELECT
|
||||||
|
CAST(l.OrderHeaderID AS NVARCHAR(50)) AS OrderHeaderID,
|
||||||
|
CAST(l.OrderLineID AS NVARCHAR(50)) AS OrderLineID,
|
||||||
|
|
||||||
|
-- Şimdilik eski alanlar boş döndürülür
|
||||||
|
CAST('' AS NVARCHAR(50)) AS OldItemCode,
|
||||||
|
CAST('' AS NVARCHAR(50)) AS OldColor,
|
||||||
|
CAST('' AS NVARCHAR(50)) AS OldDim2,
|
||||||
|
CAST('' AS NVARCHAR(250)) AS OldDesc,
|
||||||
|
|
||||||
|
ISNULL(l.ItemCode,'') AS NewItemCode,
|
||||||
|
ISNULL(l.ColorCode,'') AS NewColor,
|
||||||
|
ISNULL(l.ItemDim2Code,'') AS NewDim2,
|
||||||
|
ISNULL(l.LineDescription,'') AS NewDesc
|
||||||
|
FROM dbo.trOrderLine l
|
||||||
|
WHERE l.OrderHeaderID = @p1
|
||||||
|
AND ISNULL(l.ItemCode,'') LIKE 'U%'
|
||||||
|
ORDER BY l.SortOrder, l.OrderLineID
|
||||||
|
`, orderHeaderID)
|
||||||
|
}
|
||||||
269
svc/queries/orderproductionlist.go
Normal file
269
svc/queries/orderproductionlist.go
Normal file
@@ -0,0 +1,269 @@
|
|||||||
|
package queries
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/auth"
|
||||||
|
"bssapp-backend/internal/authz"
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ========================================================
|
||||||
|
// 📌 GetOrderProductionList — Üretime verilecek ürün içeren siparişler
|
||||||
|
// ========================================================
|
||||||
|
func GetOrderProductionList(
|
||||||
|
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 {
|
||||||
|
piyasaWhere = "1=0"
|
||||||
|
} else {
|
||||||
|
piyasaWhere = authz.BuildINClause(
|
||||||
|
"UPPER(f2.CustomerAtt01)",
|
||||||
|
codes,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------
|
||||||
|
// 📄 BASE QUERY (orderlist.go ile aynı kolonlar)
|
||||||
|
// ----------------------------------------------------
|
||||||
|
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(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,
|
||||||
|
CAST(1 AS bit) AS HasUretimUrunu,
|
||||||
|
ISNULL(h.Description,'') AS Description,
|
||||||
|
|
||||||
|
usd.Rate AS ExchangeRateUSD
|
||||||
|
|
||||||
|
FROM dbo.trOrderHeader h
|
||||||
|
|
||||||
|
-- ✅ TOPLAM ARTIK trOrderLineCurrency'den: CurrencyCode = DocCurrencyCode
|
||||||
|
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'
|
||||||
|
|
||||||
|
-- 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
|
||||||
|
)
|
||||||
|
-- ✅ Üretime verilecek ürün filtresi
|
||||||
|
AND EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM dbo.trOrderLine l2
|
||||||
|
WHERE l2.OrderHeaderID = h.OrderHeaderID
|
||||||
|
AND ISNULL(l2.ItemCode,'') LIKE 'U%%'
|
||||||
|
)
|
||||||
|
`, 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)
|
||||||
|
}
|
||||||
66
svc/routes/orderproductionitems.go
Normal file
66
svc/routes/orderproductionitems.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/models"
|
||||||
|
"bssapp-backend/queries"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
// 📌 OrderProductionItemsRoute — U ürün satırları
|
||||||
|
// ======================================================
|
||||||
|
func OrderProductionItemsRoute(mssql *sql.DB) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
id := mux.Vars(r)["id"]
|
||||||
|
if id == "" {
|
||||||
|
http.Error(w, "OrderHeaderID bulunamadı", http.StatusBadRequest)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := queries.GetOrderProductionItems(mssql, id)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ SQL sorgu hatası: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
list := make([]models.OrderProductionItem, 0, 100)
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var o models.OrderProductionItem
|
||||||
|
if err := rows.Scan(
|
||||||
|
&o.OrderHeaderID,
|
||||||
|
&o.OrderLineID,
|
||||||
|
&o.OldItemCode,
|
||||||
|
&o.OldColor,
|
||||||
|
&o.OldDim2,
|
||||||
|
&o.OldDesc,
|
||||||
|
&o.NewItemCode,
|
||||||
|
&o.NewColor,
|
||||||
|
&o.NewDim2,
|
||||||
|
&o.NewDesc,
|
||||||
|
); err != nil {
|
||||||
|
log.Printf("⚠️ SCAN HATASI: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
list = append(list, o)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("⚠️ rows.Err(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.NewEncoder(w).Encode(list); err != nil {
|
||||||
|
log.Printf("❌ encode error: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
130
svc/routes/orderproductionlist.go
Normal file
130
svc/routes/orderproductionlist.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package routes
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bssapp-backend/auth"
|
||||||
|
"bssapp-backend/db"
|
||||||
|
"bssapp-backend/models"
|
||||||
|
"bssapp-backend/queries"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ======================================================
|
||||||
|
// 📌 OrderProductionListRoute — Üretime verilecek siparişler
|
||||||
|
// ======================================================
|
||||||
|
func OrderProductionListRoute(mssql *sql.DB) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// 🔍 Query Param (RAW + TRIM)
|
||||||
|
// --------------------------------------------------
|
||||||
|
raw := r.URL.Query().Get("search")
|
||||||
|
search := strings.TrimSpace(raw)
|
||||||
|
|
||||||
|
log.Printf(
|
||||||
|
"📥 /api/orders/production-list search raw=%q trimmed=%q lenRaw=%d lenTrim=%d",
|
||||||
|
raw,
|
||||||
|
search,
|
||||||
|
len(raw),
|
||||||
|
len(search),
|
||||||
|
)
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// 🗄 SQL CALL (WITH CONTEXT)
|
||||||
|
// --------------------------------------------------
|
||||||
|
rows, err := queries.GetOrderProductionList(
|
||||||
|
r.Context(),
|
||||||
|
mssql,
|
||||||
|
db.PgDB,
|
||||||
|
search,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("❌ SQL sorgu hatası: %v", err)
|
||||||
|
http.Error(w, "Veritabanı hatası", http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// 📦 Sonuç Listesi
|
||||||
|
// --------------------------------------------------
|
||||||
|
list := make([]models.OrderList, 0, 100)
|
||||||
|
count := 0
|
||||||
|
|
||||||
|
// ==================================================
|
||||||
|
// 🧠 SCAN — SQL SELECT ile BİRE BİR (18 kolon)
|
||||||
|
// ==================================================
|
||||||
|
for rows.Next() {
|
||||||
|
|
||||||
|
var o models.OrderList
|
||||||
|
|
||||||
|
err = rows.Scan(
|
||||||
|
&o.OrderHeaderID, // 1
|
||||||
|
&o.OrderNumber, // 2
|
||||||
|
&o.OrderDate, // 3
|
||||||
|
|
||||||
|
&o.CurrAccCode, // 4
|
||||||
|
&o.CurrAccDescription, // 5
|
||||||
|
|
||||||
|
&o.MusteriTemsilcisi, // 6
|
||||||
|
&o.Piyasa, // 7
|
||||||
|
|
||||||
|
&o.CreditableConfirmedDate, // 8
|
||||||
|
&o.DocCurrencyCode, // 9
|
||||||
|
|
||||||
|
&o.TotalAmount, // 10
|
||||||
|
&o.TotalAmountUSD, // 11
|
||||||
|
&o.PackedAmount, // 12
|
||||||
|
&o.PackedUSD, // 13
|
||||||
|
&o.PackedRatePct, // 14
|
||||||
|
|
||||||
|
&o.IsCreditableConfirmed, // 15
|
||||||
|
&o.HasUretimUrunu, // 16
|
||||||
|
&o.Description, // 17
|
||||||
|
|
||||||
|
&o.ExchangeRateUSD, // 18
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(
|
||||||
|
"⚠️ SCAN HATASI | OrderHeaderID=%v | err=%v",
|
||||||
|
o.OrderHeaderID,
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
list = append(list, o)
|
||||||
|
count++
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Err(); err != nil {
|
||||||
|
log.Printf("⚠️ rows.Err(): %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// 📊 RESULT LOG
|
||||||
|
// --------------------------------------------------
|
||||||
|
claims, _ := auth.GetClaimsFromContext(r.Context())
|
||||||
|
|
||||||
|
log.Printf(
|
||||||
|
"✅ Order production list DONE | user=%d | search=%q | resultCount=%d",
|
||||||
|
claims.ID,
|
||||||
|
search,
|
||||||
|
count,
|
||||||
|
)
|
||||||
|
|
||||||
|
// --------------------------------------------------
|
||||||
|
// ✅ JSON RESPONSE
|
||||||
|
// --------------------------------------------------
|
||||||
|
if err := json.NewEncoder(w).Encode(list); err != nil {
|
||||||
|
log.Printf("❌ encode error: %v", err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
125
ui/quasar.config.js.temporary.compiled.1771513237059.mjs
Normal file
125
ui/quasar.config.js.temporary.compiled.1771513237059.mjs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
/**
|
||||||
|
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||||
|
* 1. DO NOT edit this file directly as it won't do anything.
|
||||||
|
* 2. EDIT the original quasar.config file INSTEAD.
|
||||||
|
* 3. DO NOT git commit this file. It should be ignored.
|
||||||
|
*
|
||||||
|
* This file is still here because there was an error in
|
||||||
|
* the original quasar.config file and this allows you to
|
||||||
|
* investigate the Node.js stack error.
|
||||||
|
*
|
||||||
|
* After you fix the original file, this file will be
|
||||||
|
* deleted automatically.
|
||||||
|
**/
|
||||||
|
|
||||||
|
|
||||||
|
// quasar.config.js
|
||||||
|
import { defineConfig } from "@quasar/app-webpack/wrappers";
|
||||||
|
var quasar_config_default = defineConfig(() => {
|
||||||
|
const apiBaseUrl = (process.env.VITE_API_BASE_URL || "/api").trim();
|
||||||
|
return {
|
||||||
|
/* =====================================================
|
||||||
|
APP INFO
|
||||||
|
===================================================== */
|
||||||
|
productName: "Baggi BSS",
|
||||||
|
productDescription: "Baggi Tekstil Business Support System",
|
||||||
|
/* =====================================================
|
||||||
|
BOOT FILES
|
||||||
|
===================================================== */
|
||||||
|
boot: ["dayjs"],
|
||||||
|
/* =====================================================
|
||||||
|
GLOBAL CSS
|
||||||
|
===================================================== */
|
||||||
|
css: ["app.css"],
|
||||||
|
/* =====================================================
|
||||||
|
ICONS / FONTS
|
||||||
|
===================================================== */
|
||||||
|
extras: [
|
||||||
|
"roboto-font",
|
||||||
|
"material-icons"
|
||||||
|
],
|
||||||
|
/* =====================================================
|
||||||
|
BUILD (PRODUCTION)
|
||||||
|
===================================================== */
|
||||||
|
build: {
|
||||||
|
vueRouterMode: "hash",
|
||||||
|
env: {
|
||||||
|
VITE_API_BASE_URL: apiBaseUrl
|
||||||
|
},
|
||||||
|
esbuildTarget: {
|
||||||
|
browser: ["es2022", "firefox115", "chrome115", "safari14"],
|
||||||
|
node: "node20"
|
||||||
|
},
|
||||||
|
// Cache & performance
|
||||||
|
gzip: true,
|
||||||
|
preloadChunks: true
|
||||||
|
},
|
||||||
|
/* =====================================================
|
||||||
|
DEV SERVER (LOCAL)
|
||||||
|
===================================================== */
|
||||||
|
devServer: {
|
||||||
|
server: { type: "http" },
|
||||||
|
port: 9e3,
|
||||||
|
open: true,
|
||||||
|
// DEV proxy (CORS'suz)
|
||||||
|
proxy: [
|
||||||
|
{
|
||||||
|
context: ["/api"],
|
||||||
|
target: "http://localhost:8080",
|
||||||
|
changeOrigin: true,
|
||||||
|
secure: false
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
/* =====================================================
|
||||||
|
QUASAR FRAMEWORK
|
||||||
|
===================================================== */
|
||||||
|
framework: {
|
||||||
|
config: {
|
||||||
|
notify: {
|
||||||
|
position: "top",
|
||||||
|
timeout: 2500
|
||||||
|
}
|
||||||
|
},
|
||||||
|
lang: "tr",
|
||||||
|
plugins: [
|
||||||
|
"Loading",
|
||||||
|
"Dialog",
|
||||||
|
"Notify"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
animations: [],
|
||||||
|
/* =====================================================
|
||||||
|
SSR / PWA (DISABLED)
|
||||||
|
===================================================== */
|
||||||
|
ssr: {
|
||||||
|
prodPort: 3e3,
|
||||||
|
middlewares: ["render"],
|
||||||
|
pwa: false
|
||||||
|
},
|
||||||
|
pwa: {
|
||||||
|
workboxMode: "GenerateSW"
|
||||||
|
},
|
||||||
|
/* =====================================================
|
||||||
|
MOBILE / DESKTOP
|
||||||
|
===================================================== */
|
||||||
|
capacitor: {
|
||||||
|
hideSplashscreen: true
|
||||||
|
},
|
||||||
|
electron: {
|
||||||
|
preloadScripts: ["electron-preload"],
|
||||||
|
inspectPort: 5858,
|
||||||
|
bundler: "packager",
|
||||||
|
builder: {
|
||||||
|
appId: "baggisowtfaresystem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bex: {
|
||||||
|
extraScripts: []
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
export {
|
||||||
|
quasar_config_default as default
|
||||||
|
};
|
||||||
57
ui/src/pages/OrderProductionUpdate.vue
Normal file
57
ui/src/pages/OrderProductionUpdate.vue
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
<template>
|
||||||
|
<q-page class="q-pa-md">
|
||||||
|
<div class="text-h6 text-weight-bold">Üretime Verilen Ürünler</div>
|
||||||
|
<div class="text-caption text-grey-7 q-mt-xs">
|
||||||
|
OrderHeaderID: {{ orderHeaderID || '-' }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
class="q-mt-md"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
dense
|
||||||
|
separator="cell"
|
||||||
|
row-key="OrderLineID"
|
||||||
|
:rows="store.items"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="store.loading"
|
||||||
|
no-data-label="Üretime verilecek ürün bulunamadı"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-banner v-if="store.error" class="bg-red text-white q-mt-sm">
|
||||||
|
Hata: {{ store.error }}
|
||||||
|
</q-banner>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, watch } from 'vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { useOrderProductionItemStore } from 'src/stores/OrderProductionItemStore'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const store = useOrderProductionItemStore()
|
||||||
|
|
||||||
|
const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim())
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'OldItemCode', label: 'Eski Ürün Kodu', field: 'OldItemCode', align: 'left', sortable: true, style: 'min-width:140px;white-space:nowrap', headerStyle: 'min-width:140px;white-space:nowrap' },
|
||||||
|
{ name: 'OldColor', label: 'Eski Ürün Rengi', field: 'OldColor', align: 'left', sortable: true, style: 'min-width:120px;white-space:nowrap', headerStyle: 'min-width:120px;white-space:nowrap' },
|
||||||
|
{ name: 'OldDim2', label: 'Eski 2. Renk', field: 'OldDim2', align: 'left', sortable: true, style: 'min-width:110px;white-space:nowrap', headerStyle: 'min-width:110px;white-space:nowrap' },
|
||||||
|
{ name: 'OldDesc', label: 'Eski Açıklama', field: 'OldDesc', align: 'left', sortable: false, style: 'min-width:180px;white-space:nowrap', headerStyle: 'min-width:180px;white-space:nowrap' },
|
||||||
|
{ name: 'NewItemCode', label: 'Yeni Ürün Kodu', field: 'NewItemCode', align: 'left', sortable: true, style: 'min-width:140px;white-space:nowrap', headerStyle: 'min-width:140px;white-space:nowrap' },
|
||||||
|
{ name: 'NewColor', label: 'Yeni Ürün Rengi', field: 'NewColor', align: 'left', sortable: true, style: 'min-width:120px;white-space:nowrap', headerStyle: 'min-width:120px;white-space:nowrap' },
|
||||||
|
{ name: 'NewDim2', label: 'Yeni 2. Renk', field: 'NewDim2', align: 'left', sortable: true, style: 'min-width:110px;white-space:nowrap', headerStyle: 'min-width:110px;white-space:nowrap' },
|
||||||
|
{ name: 'NewDesc', label: 'Yeni Açıklama', field: 'NewDesc', align: 'left', sortable: false, style: 'min-width:180px;white-space:nowrap', headerStyle: 'min-width:180px;white-space:nowrap' }
|
||||||
|
]
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
store.fetchItems(orderHeaderID.value)
|
||||||
|
})
|
||||||
|
|
||||||
|
watch(orderHeaderID, (id) => {
|
||||||
|
store.fetchItems(id)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
357
ui/src/pages/OrderProductionUpdateList.vue
Normal file
357
ui/src/pages/OrderProductionUpdateList.vue
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
<template>
|
||||||
|
<q-page v-if="canReadOrder" class="ol-page">
|
||||||
|
<div class="ol-filter-bar">
|
||||||
|
<div class="ol-filter-row">
|
||||||
|
<q-input
|
||||||
|
v-model="store.filters.search"
|
||||||
|
class="ol-filter-input ol-search"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
debounce="300"
|
||||||
|
clearable
|
||||||
|
label="Arama (Sipariş No / Cari / Açıklama)"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<q-icon name="search" />
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-model="store.filters.CurrAccCode"
|
||||||
|
class="ol-filter-input"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
clearable
|
||||||
|
label="Cari Kodu"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-input
|
||||||
|
v-model="store.filters.OrderDate"
|
||||||
|
class="ol-filter-input"
|
||||||
|
dense
|
||||||
|
filled
|
||||||
|
type="date"
|
||||||
|
label="Sipariş Tarihi"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="ol-filter-actions">
|
||||||
|
<q-btn
|
||||||
|
label="Temizle"
|
||||||
|
icon="clear"
|
||||||
|
color="grey-7"
|
||||||
|
flat
|
||||||
|
:disable="store.loading"
|
||||||
|
@click="clearFilters"
|
||||||
|
>
|
||||||
|
<q-tooltip>Tüm filtreleri temizle</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
label="Yenile"
|
||||||
|
color="primary"
|
||||||
|
icon="refresh"
|
||||||
|
:loading="store.loading"
|
||||||
|
@click="store.fetchOrders"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<q-btn
|
||||||
|
label="Excel'e Aktar"
|
||||||
|
icon="download"
|
||||||
|
color="primary"
|
||||||
|
outline
|
||||||
|
:disable="store.loading || productionOrders.length === 0"
|
||||||
|
@click="exportExcel"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ol-filter-total">
|
||||||
|
<div class="ol-total-line">
|
||||||
|
<span class="ol-total-label">Toplam USD:</span>
|
||||||
|
<strong class="ol-total-value">
|
||||||
|
{{ totalVisibleUSD.toLocaleString('tr-TR', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) }}
|
||||||
|
</strong>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<q-table
|
||||||
|
title="Üretime Verilecek Ürünleri Olan Siparişler"
|
||||||
|
class="ol-table"
|
||||||
|
flat
|
||||||
|
bordered
|
||||||
|
dense
|
||||||
|
separator="cell"
|
||||||
|
row-key="OrderHeaderID"
|
||||||
|
:rows="productionOrders"
|
||||||
|
:columns="columns"
|
||||||
|
:loading="store.loading"
|
||||||
|
no-data-label="Üretime verilecek sipariş bulunamadı"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #body-cell-open="props">
|
||||||
|
<q-td :props="props" class="text-center">
|
||||||
|
<q-btn
|
||||||
|
icon="open_in_new"
|
||||||
|
color="primary"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="selectOrder(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Siparişi Aç</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-IsCreditableConfirmed="props">
|
||||||
|
<q-td :props="props" class="text-center q-gutter-sm">
|
||||||
|
<q-btn
|
||||||
|
icon="picture_as_pdf"
|
||||||
|
color="red"
|
||||||
|
flat
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
@click="printPDF(props.row)"
|
||||||
|
>
|
||||||
|
<q-tooltip>Siparişi PDF olarak aç</q-tooltip>
|
||||||
|
</q-btn>
|
||||||
|
|
||||||
|
<q-icon
|
||||||
|
:name="props.row.IsCreditableConfirmed ? 'check_circle' : 'cancel'"
|
||||||
|
:color="props.row.IsCreditableConfirmed ? 'green' : 'red'"
|
||||||
|
size="20px"
|
||||||
|
>
|
||||||
|
<q-tooltip>
|
||||||
|
{{ props.row.IsCreditableConfirmed ? 'Onaylı' : 'Onaysız' }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-icon>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-OrderDate="props">
|
||||||
|
<q-td :props="props" class="text-center">
|
||||||
|
{{ formatDate(props.row.OrderDate) }}
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-CreditableConfirmedDate="props">
|
||||||
|
<q-td :props="props" class="text-center">
|
||||||
|
{{ formatDate(props.row.CreditableConfirmedDate) }}
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
|
<template #body-cell-CurrAccDescription="props">
|
||||||
|
<q-td :props="props" class="ol-col-cari">
|
||||||
|
<div class="ol-col-multiline">{{ props.value }}</div>
|
||||||
|
<q-tooltip v-if="props.value">
|
||||||
|
{{ props.value }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-MusteriTemsilcisi="props">
|
||||||
|
<q-td :props="props" class="ol-col-short">
|
||||||
|
<div class="ol-col-multiline">{{ props.value }}</div>
|
||||||
|
<q-tooltip v-if="props.value">
|
||||||
|
{{ props.value }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-Piyasa="props">
|
||||||
|
<q-td :props="props" class="ol-col-short">
|
||||||
|
<div class="ol-col-multiline">{{ props.value }}</div>
|
||||||
|
<q-tooltip v-if="props.value">
|
||||||
|
{{ props.value }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-Description="props">
|
||||||
|
<q-td :props="props" class="ol-col-desc">
|
||||||
|
<div class="ol-col-multiline">{{ props.value }}</div>
|
||||||
|
<q-tooltip v-if="props.value">
|
||||||
|
{{ props.value }}
|
||||||
|
</q-tooltip>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #body-cell-HasUretimUrunu="props">
|
||||||
|
<q-td :props="props" class="text-center">
|
||||||
|
<q-icon
|
||||||
|
:name="props.row.HasUretimUrunu ? 'check_circle' : 'cancel'"
|
||||||
|
:color="props.row.HasUretimUrunu ? 'green' : 'grey-5'"
|
||||||
|
size="18px"
|
||||||
|
/>
|
||||||
|
</q-td>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
|
||||||
|
<q-banner v-if="store.error" class="bg-red text-white q-mt-sm">
|
||||||
|
Hata: {{ store.error }}
|
||||||
|
</q-banner>
|
||||||
|
</q-page>
|
||||||
|
|
||||||
|
<q-page v-else class="q-pa-md flex flex-center">
|
||||||
|
<div class="text-negative text-subtitle1">
|
||||||
|
Bu modüle erişim yetkiniz yok.
|
||||||
|
</div>
|
||||||
|
</q-page>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { computed, onMounted, watch } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useQuasar } from 'quasar'
|
||||||
|
import { useOrderProductionUpdateStore } from 'src/stores/OrderProductionUpdateStore'
|
||||||
|
import { useAuthStore } from 'src/stores/authStore'
|
||||||
|
import { usePermission } from 'src/composables/usePermission'
|
||||||
|
import api, { extractApiErrorDetail } from 'src/services/api'
|
||||||
|
|
||||||
|
const { canRead } = usePermission()
|
||||||
|
const canReadOrder = canRead('order')
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const $q = useQuasar()
|
||||||
|
const store = useOrderProductionUpdateStore()
|
||||||
|
|
||||||
|
let searchTimer = null
|
||||||
|
watch(
|
||||||
|
() => store.filters.search,
|
||||||
|
() => {
|
||||||
|
clearTimeout(searchTimer)
|
||||||
|
searchTimer = setTimeout(() => {
|
||||||
|
store.fetchOrders()
|
||||||
|
}, 400)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const productionOrders = computed(() => store.filteredOrders)
|
||||||
|
|
||||||
|
const totalVisibleUSD = computed(() =>
|
||||||
|
productionOrders.value.reduce((sum, o) => {
|
||||||
|
const v = Number(o.TotalAmountUSD || 0)
|
||||||
|
return sum + (Number.isFinite(v) ? v : 0)
|
||||||
|
}, 0)
|
||||||
|
)
|
||||||
|
|
||||||
|
function clearFilters () {
|
||||||
|
store.filters.search = ''
|
||||||
|
store.filters.CurrAccCode = ''
|
||||||
|
store.filters.OrderDate = ''
|
||||||
|
store.fetchOrders()
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDate (v) {
|
||||||
|
if (!v) return ''
|
||||||
|
return String(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectOrder (row) {
|
||||||
|
if (!row?.OrderHeaderID) {
|
||||||
|
$q.notify({ type: 'warning', message: 'OrderHeaderID bulunamadı' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
router.push({
|
||||||
|
name: 'orderproductionupdate',
|
||||||
|
params: { orderHeaderID: row.OrderHeaderID }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async function printPDF (row) {
|
||||||
|
try {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
if (!auth?.token) {
|
||||||
|
$q.notify({ type: 'warning', message: 'Oturum bulunamadı' })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await api.get(`/order/pdf/${row.OrderHeaderID}`, {
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
|
||||||
|
const blob = new Blob([res.data], { type: 'application/pdf' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
window.open(url, '_blank')
|
||||||
|
setTimeout(() => URL.revokeObjectURL(url), 2000)
|
||||||
|
} catch (err) {
|
||||||
|
const status = err?.response?.status
|
||||||
|
const detail = extractApiErrorDetail(err)
|
||||||
|
console.error(`PDF load error [${status}] /order/pdf/${row?.OrderHeaderID}: ${detail}`)
|
||||||
|
$q.notify({ type: 'negative', message: `PDF alınamadı: ${detail}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportExcel () {
|
||||||
|
const auth = useAuthStore()
|
||||||
|
|
||||||
|
if (!auth?.token) {
|
||||||
|
$q.notify({
|
||||||
|
type: 'negative',
|
||||||
|
message: 'Oturum bulunamadı'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const params = new URLSearchParams()
|
||||||
|
if (store.filters.search) params.append('search', store.filters.search)
|
||||||
|
|
||||||
|
api.get(`/orders/production-list?${params.toString()}`, {
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then((res) => {
|
||||||
|
const blob = new Blob([res.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||||
|
const url = URL.createObjectURL(blob)
|
||||||
|
const a = document.createElement('a')
|
||||||
|
a.href = url
|
||||||
|
a.download = 'uretime_verilecek_siparisler.xlsx'
|
||||||
|
a.click()
|
||||||
|
URL.revokeObjectURL(url)
|
||||||
|
}).catch((err) => {
|
||||||
|
const detail = extractApiErrorDetail(err)
|
||||||
|
$q.notify({ type: 'negative', message: `Excel alınamadı: ${detail}` })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
store.fetchOrders()
|
||||||
|
})
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{ name: 'open', label: '', field: 'open', align: 'center', sortable: false },
|
||||||
|
{ name: 'OrderNumber', label: 'Sipariş No', field: 'OrderNumber', align: 'left', sortable: true, style: 'min-width:108px;white-space:nowrap', headerStyle: 'min-width:108px;white-space:nowrap' },
|
||||||
|
{ name: 'OrderDate', label: 'Tarih', field: 'OrderDate', align: 'center', sortable: true, style: 'min-width:82px;white-space:nowrap', headerStyle: 'min-width:82px;white-space:nowrap' },
|
||||||
|
{ name: 'CurrAccCode', label: 'Cari Kod', field: 'CurrAccCode', align: 'left', sortable: true, style: 'min-width:82px;white-space:nowrap', headerStyle: 'min-width:82px;white-space:nowrap' },
|
||||||
|
{ name: 'CurrAccDescription', label: 'Cari Adı', field: 'CurrAccDescription', align: 'left', sortable: true, classes: 'ol-col-cari', headerClasses: 'ol-col-cari', style: 'width:160px;max-width:160px', headerStyle: 'width:160px;max-width:160px' },
|
||||||
|
{ name: 'MusteriTemsilcisi', label: 'Temsilci', field: 'MusteriTemsilcisi', align: 'left', sortable: true, classes: 'ol-col-short', headerClasses: 'ol-col-short', style: 'width:88px;max-width:88px', headerStyle: 'width:88px;max-width:88px' },
|
||||||
|
{ name: 'Piyasa', label: 'Piyasa', field: 'Piyasa', align: 'left', sortable: true, classes: 'ol-col-short', headerClasses: 'ol-col-short', style: 'width:72px;max-width:72px', headerStyle: 'width:72px;max-width:72px' },
|
||||||
|
{ name: 'CreditableConfirmedDate', label: 'Onay', field: 'CreditableConfirmedDate', align: 'center', sortable: true, style: 'min-width:86px;white-space:nowrap', headerStyle: 'min-width:86px;white-space:nowrap' },
|
||||||
|
{ name: 'DocCurrencyCode', label: 'PB', field: 'DocCurrencyCode', align: 'center', sortable: true, style: 'min-width:46px;white-space:nowrap', headerStyle: 'min-width:46px;white-space:nowrap' },
|
||||||
|
{
|
||||||
|
name: 'TotalAmount',
|
||||||
|
label: 'Tutar',
|
||||||
|
field: 'TotalAmount',
|
||||||
|
align: 'right',
|
||||||
|
sortable: true,
|
||||||
|
style: 'min-width:120px;white-space:nowrap',
|
||||||
|
headerStyle: 'min-width:120px;white-space:nowrap',
|
||||||
|
format: (val, row) => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' ' + row.DocCurrencyCode
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'TotalAmountUSD',
|
||||||
|
label: 'Tutar (USD)',
|
||||||
|
field: 'TotalAmountUSD',
|
||||||
|
align: 'right',
|
||||||
|
sortable: true,
|
||||||
|
style: 'min-width:120px;white-space:nowrap',
|
||||||
|
headerStyle: 'min-width:120px;white-space:nowrap',
|
||||||
|
format: val => Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
|
||||||
|
},
|
||||||
|
{ name: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true },
|
||||||
|
{ name: 'HasUretimUrunu', label: 'Üretim', field: 'HasUretimUrunu', align: 'left', sortable: true, style: 'min-width:190px;white-space:nowrap', headerStyle: 'min-width:190px;white-space:nowrap' },
|
||||||
|
{ name: 'Description', label: 'Açıklama', field: 'Description', align: 'left', sortable: false, classes: 'ol-col-desc', headerClasses: 'ol-col-desc', style: 'width:160px;max-width:160px', headerStyle: 'width:160px;max-width:160px' }
|
||||||
|
]
|
||||||
|
</script>
|
||||||
34
ui/src/stores/OrderProductionItemStore.js
Normal file
34
ui/src/stores/OrderProductionItemStore.js
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// src/stores/OrderProductionItemStore.js
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import api from 'src/services/api'
|
||||||
|
|
||||||
|
export const useOrderProductionItemStore = defineStore('orderproductionitems', {
|
||||||
|
state: () => ({
|
||||||
|
items: [],
|
||||||
|
loading: false,
|
||||||
|
error: null
|
||||||
|
}),
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async fetchItems (orderHeaderID) {
|
||||||
|
if (!orderHeaderID) {
|
||||||
|
this.items = []
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
this.error = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await api.get(`/orders/production-items/${encodeURIComponent(orderHeaderID)}`)
|
||||||
|
const data = res?.data
|
||||||
|
this.items = Array.isArray(data) ? data : []
|
||||||
|
} catch (err) {
|
||||||
|
this.items = []
|
||||||
|
this.error = err?.response?.data || err?.message || 'Liste alınamadı'
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
166
ui/src/stores/OrderProductionUpdateStore.js
Normal file
166
ui/src/stores/OrderProductionUpdateStore.js
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
// src/stores/OrderProductionUpdateStore.js
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import api from 'src/services/api'
|
||||||
|
|
||||||
|
let lastRequestId = 0
|
||||||
|
|
||||||
|
export const useOrderProductionUpdateStore = defineStore('orderproductionupdate', {
|
||||||
|
state: () => ({
|
||||||
|
orders: [],
|
||||||
|
loading: false,
|
||||||
|
error: null,
|
||||||
|
|
||||||
|
filters: {
|
||||||
|
search: '',
|
||||||
|
CurrAccCode: '',
|
||||||
|
OrderDate: ''
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
getters: {
|
||||||
|
filteredOrders (state) {
|
||||||
|
let result = state.orders
|
||||||
|
|
||||||
|
if (state.filters.CurrAccCode) {
|
||||||
|
result = result.filter(o => o.CurrAccCode === state.filters.CurrAccCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.filters.OrderDate) {
|
||||||
|
result = result.filter(o =>
|
||||||
|
o.OrderDate?.startsWith(state.filters.OrderDate)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
},
|
||||||
|
|
||||||
|
totalVisibleUSD () {
|
||||||
|
return this.filteredOrders.reduce((sum, o) => {
|
||||||
|
const value = Number(o.TotalAmountUSD || 0)
|
||||||
|
return sum + (Number.isFinite(value) ? value : 0)
|
||||||
|
}, 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
totalPackedVisibleUSD () {
|
||||||
|
return this.filteredOrders.reduce((sum, o) => {
|
||||||
|
const value = Number(o.PackedUSD || 0)
|
||||||
|
return sum + (Number.isFinite(value) ? value : 0)
|
||||||
|
}, 0)
|
||||||
|
},
|
||||||
|
|
||||||
|
packedVisibleRatePct () {
|
||||||
|
if (!this.totalVisibleUSD) return 0
|
||||||
|
return (this.totalPackedVisibleUSD / this.totalVisibleUSD) * 100
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
async fetchOrders () {
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 REQUEST ID
|
||||||
|
// ==============================
|
||||||
|
const rid = ++lastRequestId
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 SEARCH SNAPSHOT
|
||||||
|
// ==============================
|
||||||
|
const raw = this.filters.search ?? ''
|
||||||
|
const trimmed = String(raw).trim()
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 REQUEST LOG
|
||||||
|
// ==============================
|
||||||
|
console.groupCollapsed(
|
||||||
|
`%c[orders-prod] FETCH rid=${rid}`,
|
||||||
|
'color:#1976d2;font-weight:bold'
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('raw =', JSON.stringify(raw), 'len=', String(raw).length)
|
||||||
|
console.log('trimmed =', JSON.stringify(trimmed), 'len=', trimmed.length)
|
||||||
|
console.log('filters =', JSON.parse(JSON.stringify(this.filters)))
|
||||||
|
console.log('lastRID =', lastRequestId)
|
||||||
|
|
||||||
|
console.groupEnd()
|
||||||
|
|
||||||
|
this.loading = true
|
||||||
|
this.error = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 PARAMS
|
||||||
|
// ==============================
|
||||||
|
const params = {}
|
||||||
|
if (trimmed) params.search = trimmed
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 API CALL
|
||||||
|
// ==============================
|
||||||
|
const res = await api.get('/orders/production-list', { params })
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 STALE CHECK
|
||||||
|
// ==============================
|
||||||
|
if (rid !== lastRequestId) {
|
||||||
|
console.warn(
|
||||||
|
`[orders-prod] IGNORE stale response rid=${rid} last=${lastRequestId}`
|
||||||
|
)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 DATA
|
||||||
|
// ==============================
|
||||||
|
const data = res?.data
|
||||||
|
this.orders = Array.isArray(data) ? data : []
|
||||||
|
|
||||||
|
// ==============================
|
||||||
|
// 📌 RESPONSE LOG
|
||||||
|
// ==============================
|
||||||
|
console.groupCollapsed(
|
||||||
|
`%c[orders-prod] RESPONSE rid=${rid} count=${this.orders.length}`,
|
||||||
|
'color:#2e7d32;font-weight:bold'
|
||||||
|
)
|
||||||
|
|
||||||
|
console.log('status =', res?.status)
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'sample =',
|
||||||
|
this.orders.slice(0, 5).map(o => ({
|
||||||
|
id: o.OrderHeaderID,
|
||||||
|
no: o.OrderNumber,
|
||||||
|
code: o.CurrAccCode,
|
||||||
|
name: o.CurrAccDescription
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
|
||||||
|
console.groupEnd()
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
if (rid !== lastRequestId) return
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
'[orders-prod] FETCH FAILED',
|
||||||
|
err?.response?.status,
|
||||||
|
err?.response?.data || err
|
||||||
|
)
|
||||||
|
|
||||||
|
this.orders = []
|
||||||
|
|
||||||
|
this.error =
|
||||||
|
err?.response?.data ||
|
||||||
|
err?.message ||
|
||||||
|
'Sipariş listesi alınamadı'
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
|
||||||
|
if (rid === lastRequestId) {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user