Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -18,7 +18,8 @@ func BuildClaimsFromUser(u *models.MkUser, ttl time.Duration) Claims {
|
||||
Username: u.Username,
|
||||
RoleCode: u.RoleCode,
|
||||
RoleID: u.RoleID,
|
||||
// ✅ BURASI
|
||||
V3Username: u.V3Username,
|
||||
V3UserGroup: u.V3UserGroup,
|
||||
DepartmentCodes: u.DepartmentCodes,
|
||||
|
||||
SessionID: u.SessionID,
|
||||
|
||||
@@ -55,6 +55,7 @@ type OrderDetail struct {
|
||||
LDisRate5 NullFloat64 `json:"LDisRate5"`
|
||||
|
||||
DocCurrencyCode NullString `json:"DocCurrencyCode"`
|
||||
RelationCurrencyCode NullString `json:"RelationCurrencyCode"`
|
||||
PriceCurrencyCode NullString `json:"PriceCurrencyCode"`
|
||||
PriceExchangeRate NullFloat64 `json:"PriceExchangeRate"`
|
||||
|
||||
@@ -95,4 +96,29 @@ type OrderDetail struct {
|
||||
UrunAltGrubu NullString `json:"UrunAltGrubu"`
|
||||
Fit1 NullString `json:"Fit1"`
|
||||
Fit2 NullString `json:"Fit2"`
|
||||
// ============================
|
||||
// 💰 Currency / Amount Fields
|
||||
// (trOrderLineCurrency)
|
||||
// ============================
|
||||
|
||||
// Döviz bazlı
|
||||
DocPrice NullFloat64 `json:"DocPrice"` // PriceVI
|
||||
DocAmount NullFloat64 `json:"DocAmount"` // AmountVI
|
||||
|
||||
// Yerel para (TRY)
|
||||
LocalPrice NullFloat64 `json:"LocalPrice"` // Price
|
||||
LocalAmount NullFloat64 `json:"LocalAmount"` // Amount
|
||||
|
||||
// İndirimler
|
||||
LineDiscount NullFloat64 `json:"LineDiscount"` // LDiscount1
|
||||
TotalDiscount NullFloat64 `json:"TotalDiscount"` // TDiscount1
|
||||
|
||||
// Vergi / Matrah
|
||||
TaxBase NullFloat64 `json:"TaxBase"`
|
||||
VatAmount NullFloat64 `json:"VatAmount"`
|
||||
VatDeducation NullFloat64 `json:"VatDeducation"`
|
||||
NetAmount NullFloat64 `json:"NetAmount"`
|
||||
|
||||
// Genel oran
|
||||
Pct NullFloat64 `json:"Pct"`
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ type OrderList struct {
|
||||
// 💰 Tutarlar
|
||||
TotalAmount float64 `json:"TotalAmount"`
|
||||
TotalAmountUSD float64 `json:"TotalAmountUSD"`
|
||||
PackedAmount float64 `json:"PackedAmount"`
|
||||
PackedUSD float64 `json:"PackedUSD"`
|
||||
PackedRatePct float64 `json:"PackedRatePct"`
|
||||
|
||||
// 📝 Açıklama
|
||||
Description string `json:"Description"`
|
||||
|
||||
@@ -12,31 +12,221 @@ func GetOrderListExcel(
|
||||
orderDate string,
|
||||
) (*sql.Rows, error) {
|
||||
|
||||
q := OrderListBaseQuery + " AND 1=1 "
|
||||
args := []interface{}{}
|
||||
q := `
|
||||
SELECT
|
||||
CAST(h.OrderHeaderID AS NVARCHAR(50)) AS OrderHeaderID,
|
||||
ISNULL(h.OrderNumber,'') AS OrderNumber,
|
||||
CONVERT(varchar,h.OrderDate,23) AS OrderDate,
|
||||
|
||||
// SEARCH
|
||||
ISNULL(h.CurrAccCode,'') AS CurrAccCode,
|
||||
ISNULL(ca.CurrAccDescription,'') AS CurrAccDescription,
|
||||
|
||||
ISNULL(mt.AttributeDescription,'') AS MusteriTemsilcisi,
|
||||
ISNULL(py.AttributeDescription,'') AS Piyasa,
|
||||
|
||||
ISNULL(h.DocCurrencyCode,'TRY') AS DocCurrencyCode,
|
||||
|
||||
-------------------------------------------------
|
||||
-- BELGE PB TOPLAM
|
||||
-------------------------------------------------
|
||||
ISNULL(t.TotalAmount,0) AS TotalAmount,
|
||||
|
||||
-------------------------------------------------
|
||||
-- USD TOPLAM
|
||||
-------------------------------------------------
|
||||
CASE
|
||||
WHEN h.DocCurrencyCode = 'USD'
|
||||
THEN ISNULL(t.TotalAmount,0)
|
||||
|
||||
WHEN h.DocCurrencyCode = 'TRY'
|
||||
AND usd.Rate > 0
|
||||
THEN ISNULL(t.TotalTRY,0) / usd.Rate
|
||||
|
||||
WHEN h.DocCurrencyCode IN ('EUR','GBP')
|
||||
AND cur.Rate > 0
|
||||
AND usd.Rate > 0
|
||||
THEN (ISNULL(t.TotalAmount,0) * cur.Rate) / usd.Rate
|
||||
|
||||
ELSE 0
|
||||
END AS TotalAmountUSD,
|
||||
|
||||
ISNULL(t.PackedAmount,0) AS PackedAmount,
|
||||
|
||||
CASE
|
||||
WHEN h.DocCurrencyCode = 'USD'
|
||||
THEN ISNULL(t.PackedAmount,0)
|
||||
|
||||
WHEN h.DocCurrencyCode = 'TRY'
|
||||
AND usd.Rate > 0
|
||||
THEN ISNULL(t.PackedTRY,0) / usd.Rate
|
||||
|
||||
WHEN cur.Rate > 0
|
||||
AND usd.Rate > 0
|
||||
THEN (ISNULL(t.PackedAmount,0) * cur.Rate) / usd.Rate
|
||||
|
||||
ELSE 0
|
||||
END AS PackedUSD,
|
||||
|
||||
CASE
|
||||
WHEN ISNULL(t.TotalAmount,0) > 0
|
||||
THEN (ISNULL(t.PackedAmount,0) * 100.0) / NULLIF(t.TotalAmount,0)
|
||||
ELSE 0
|
||||
END AS PackedRatePct,
|
||||
|
||||
ISNULL(h.Description,'') AS Description,
|
||||
|
||||
usd.Rate AS ExchangeRateUSD
|
||||
|
||||
FROM dbo.trOrderHeader h
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
-- ✅ LINE CURRENCY TOPLAM
|
||||
-------------------------------------------------
|
||||
LEFT JOIN (
|
||||
|
||||
SELECT
|
||||
l.OrderHeaderID,
|
||||
|
||||
-- Belge para birimi toplam
|
||||
SUM(
|
||||
CASE
|
||||
WHEN c.CurrencyCode = h.DocCurrencyCode
|
||||
THEN c.NetAmount
|
||||
ELSE 0
|
||||
END
|
||||
) AS TotalAmount,
|
||||
|
||||
-- TRY toplam
|
||||
SUM(
|
||||
CASE
|
||||
WHEN c.CurrencyCode = 'TRY'
|
||||
THEN c.NetAmount
|
||||
ELSE 0
|
||||
END
|
||||
) AS TotalTRY,
|
||||
|
||||
-- Paketlenen (OrderLine IsClosed=1) belge PB toplam
|
||||
SUM(
|
||||
CASE
|
||||
WHEN ISNULL(l.IsClosed,0) = 1
|
||||
AND c.CurrencyCode = h.DocCurrencyCode
|
||||
THEN c.NetAmount
|
||||
ELSE 0
|
||||
END
|
||||
) AS PackedAmount,
|
||||
|
||||
-- Paketlenen TRY toplam
|
||||
SUM(
|
||||
CASE
|
||||
WHEN ISNULL(l.IsClosed,0) = 1
|
||||
AND c.CurrencyCode = 'TRY'
|
||||
THEN c.NetAmount
|
||||
ELSE 0
|
||||
END
|
||||
) AS PackedTRY
|
||||
|
||||
FROM dbo.trOrderLineCurrency c
|
||||
|
||||
JOIN dbo.trOrderLine l
|
||||
ON l.OrderLineID = c.OrderLineID
|
||||
|
||||
JOIN dbo.trOrderHeader h
|
||||
ON h.OrderHeaderID = l.OrderHeaderID
|
||||
|
||||
GROUP BY l.OrderHeaderID
|
||||
|
||||
) t ON t.OrderHeaderID = h.OrderHeaderID
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
-- CARİ
|
||||
-------------------------------------------------
|
||||
LEFT JOIN dbo.cdCurrAccDesc ca
|
||||
ON ca.CurrAccCode = h.CurrAccCode
|
||||
AND ca.LangCode='TR'
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
-- TEMSİLCİ / PİYASA
|
||||
-------------------------------------------------
|
||||
LEFT JOIN dbo.CustomerAttributesFilter f
|
||||
ON f.CurrAccCode = h.CurrAccCode
|
||||
|
||||
LEFT JOIN dbo.cdCurrAccAttributeDesc mt
|
||||
ON mt.CurrAccTypeCode=3
|
||||
AND mt.AttributeTypeCode=2
|
||||
AND mt.AttributeCode=f.CustomerAtt02
|
||||
AND mt.LangCode='TR'
|
||||
|
||||
LEFT JOIN dbo.cdCurrAccAttributeDesc py
|
||||
ON py.CurrAccTypeCode=3
|
||||
AND py.AttributeTypeCode=1
|
||||
AND py.AttributeCode=f.CustomerAtt01
|
||||
AND py.LangCode='TR'
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
-- USD → TRY
|
||||
-------------------------------------------------
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1 Rate
|
||||
FROM dbo.AllExchangeRates
|
||||
WHERE CurrencyCode='USD'
|
||||
AND RelationCurrencyCode='TRY'
|
||||
AND ExchangeTypeCode=6
|
||||
AND Rate>0
|
||||
AND Date<=CAST(GETDATE() AS date)
|
||||
ORDER BY Date DESC
|
||||
) usd
|
||||
|
||||
|
||||
-------------------------------------------------
|
||||
-- DOC → TRY
|
||||
-------------------------------------------------
|
||||
OUTER APPLY (
|
||||
SELECT TOP 1 Rate
|
||||
FROM dbo.AllExchangeRates
|
||||
WHERE CurrencyCode=h.DocCurrencyCode
|
||||
AND RelationCurrencyCode='TRY'
|
||||
AND ExchangeTypeCode=6
|
||||
AND Rate>0
|
||||
AND Date<=CAST(GETDATE() AS date)
|
||||
ORDER BY Date DESC
|
||||
) cur
|
||||
|
||||
|
||||
WHERE
|
||||
ISNULL(h.IsCancelOrder,0)=0
|
||||
AND h.OrderTypeCode=1
|
||||
AND h.ProcessCode='WS'
|
||||
AND h.IsClosed=0
|
||||
`
|
||||
|
||||
args := []any{}
|
||||
|
||||
// ================= SEARCH =================
|
||||
if search != "" {
|
||||
q += `
|
||||
AND (
|
||||
LOWER(h.OrderNumber) LIKE LOWER(@p1) OR
|
||||
LOWER(h.CurrAccCode) LIKE LOWER(@p1) OR
|
||||
LOWER(ca.CurrAccDescription) LIKE LOWER(@p1) OR
|
||||
LOWER(h.Description) LIKE LOWER(@p1) OR
|
||||
LOWER(mt.AttributeDescription) LIKE LOWER(@p1) OR
|
||||
LOWER(py.AttributeDescription) LIKE LOWER(@p1)
|
||||
LOWER(h.OrderNumber) LIKE LOWER(@p1)
|
||||
OR LOWER(h.CurrAccCode) LIKE LOWER(@p1)
|
||||
OR LOWER(ca.CurrAccDescription) LIKE LOWER(@p1)
|
||||
OR LOWER(h.Description) LIKE LOWER(@p1)
|
||||
OR LOWER(mt.AttributeDescription) LIKE LOWER(@p1)
|
||||
OR LOWER(py.AttributeDescription) LIKE LOWER(@p1)
|
||||
)
|
||||
`
|
||||
args = append(args, "%"+search+"%")
|
||||
}
|
||||
|
||||
// CURRACC
|
||||
// ================= CURRACC =================
|
||||
if currAcc != "" {
|
||||
q += fmt.Sprintf(" AND h.CurrAccCode = @p%d ", len(args)+1)
|
||||
args = append(args, currAcc)
|
||||
}
|
||||
|
||||
// DATE
|
||||
// ================= DATE =================
|
||||
if orderDate != "" {
|
||||
q += fmt.Sprintf(
|
||||
" AND CONVERT(varchar,h.OrderDate,23) = @p%d ",
|
||||
@@ -45,7 +235,7 @@ AND (
|
||||
args = append(args, orderDate)
|
||||
}
|
||||
|
||||
// ORDER BY SONDA
|
||||
// ================= ORDER =================
|
||||
q += " ORDER BY h.CreatedDate DESC "
|
||||
|
||||
return db.Query(q, args...)
|
||||
|
||||
@@ -8,15 +8,12 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// GetOrderByID — Sipariş başlığı (header) ve satırlarını (lines) getirir.
|
||||
// GetOrderByID returns order header and lines.
|
||||
func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, error) {
|
||||
conn := db.GetDB()
|
||||
|
||||
logger.Printf("🧾 [GetOrderByID] begin • id=%s", orderID)
|
||||
logger.Printf("[GetOrderByID] begin id=%s", orderID)
|
||||
|
||||
// =====================================================
|
||||
// HEADER (Cari adı join'li)
|
||||
// =====================================================
|
||||
var header models.OrderHeader
|
||||
qHeader := `
|
||||
SELECT
|
||||
@@ -176,22 +173,15 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
|
||||
&header.LastUpdatedDate,
|
||||
&header.IsProposalBased,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
logger.Printf("⚠️ [GetOrderByID] sipariş bulunamadı: %s", orderID)
|
||||
logger.Printf("[GetOrderByID] not found: %s", orderID)
|
||||
return nil, nil, sql.ErrNoRows
|
||||
}
|
||||
logger.Printf("❌ [GetOrderByID] header sorgu hatası: %v", err)
|
||||
logger.Printf("[GetOrderByID] header error: %v", err)
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
logger.Printf("✅ [GetOrderByID] header loaded • orderNo=%v currAcc=%v",
|
||||
header.OrderNumber, header.CurrAccCode.String)
|
||||
|
||||
// =====================================================
|
||||
// LINES
|
||||
// =====================================================
|
||||
qLines := `
|
||||
SELECT
|
||||
CAST(L.OrderLineID AS varchar(36)) AS OrderLineID,
|
||||
@@ -204,10 +194,25 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
|
||||
L.ItemDim3Code,
|
||||
L.Qty1,
|
||||
L.Qty2,
|
||||
L.Price,
|
||||
|
||||
ISNULL(CD.Price, 0) AS Price,
|
||||
ISNULL(CD.CurrencyCode, ISNULL(L.DocCurrencyCode, 'TRY')) AS DocCurrencyCode,
|
||||
ISNULL(CD.RelationCurrencyCode, ISNULL(L.DocCurrencyCode, 'TRY')) AS RelationCurrencyCode,
|
||||
ISNULL(CD.ExchangeRate, ISNULL(L.PriceExchangeRate, 1)) AS PriceExchangeRate,
|
||||
ISNULL(CD.PriceVI, ISNULL(L.Price, 0)) AS DocPrice,
|
||||
ISNULL(CD.AmountVI, ISNULL(L.Price, 0) * ISNULL(L.Qty1, 0)) AS DocAmount,
|
||||
ISNULL(CD.LDiscount1, 0) AS LineDiscount,
|
||||
ISNULL(CD.TDiscount1, 0) AS TotalDiscount,
|
||||
ISNULL(CD.TaxBase, 0) AS TaxBase,
|
||||
ISNULL(CD.Pct, 0) AS Pct,
|
||||
ISNULL(CD.Vat, 0) AS VatAmount,
|
||||
ISNULL(CD.VatDeducation, 0) AS VatDeducation,
|
||||
ISNULL(CD.NetAmount, 0) AS NetAmount,
|
||||
ISNULL(CL.Price, ISNULL(CD.Price, 0)) AS LocalPrice,
|
||||
ISNULL(CL.Amount, ISNULL(CD.Amount, 0)) AS LocalAmount,
|
||||
|
||||
L.VatRate,
|
||||
L.PCTRate,
|
||||
L.DocCurrencyCode,
|
||||
L.DeliveryDate,
|
||||
L.PlannedDateOfLading,
|
||||
L.LineDescription,
|
||||
@@ -216,12 +221,19 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
|
||||
L.CreatedDate,
|
||||
L.LastUpdatedUserName,
|
||||
L.LastUpdatedDate,
|
||||
|
||||
P.ProductAtt42Desc AS UrunIlkGrubu,
|
||||
P.ProductAtt01Desc AS UrunAnaGrubu,
|
||||
P.ProductAtt02Desc AS UrunAltGrubu,
|
||||
P.ProductAtt38Desc AS Fit1,
|
||||
P.ProductAtt39Desc AS Fit2
|
||||
FROM BAGGI_V3.dbo.trOrderLine AS L
|
||||
LEFT JOIN BAGGI_V3.dbo.trOrderLineCurrency AS CD WITH (NOLOCK)
|
||||
ON CD.OrderLineID = L.OrderLineID
|
||||
AND CD.CurrencyCode = ISNULL(NULLIF(LTRIM(RTRIM(L.DocCurrencyCode)), ''), 'TRY')
|
||||
LEFT JOIN BAGGI_V3.dbo.trOrderLineCurrency AS CL WITH (NOLOCK)
|
||||
ON CL.OrderLineID = L.OrderLineID
|
||||
AND CL.CurrencyCode = 'TRY'
|
||||
LEFT JOIN ProductFilterWithDescription('TR') AS P
|
||||
ON LTRIM(RTRIM(P.ProductCode)) = LTRIM(RTRIM(L.ItemCode))
|
||||
WHERE L.OrderHeaderID = @p1
|
||||
@@ -230,7 +242,7 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
|
||||
|
||||
rows, err := conn.Query(qLines, orderID)
|
||||
if err != nil {
|
||||
logger.Printf("❌ [GetOrderByID] line sorgu hatası: %v", err)
|
||||
logger.Printf("[GetOrderByID] lines error: %v", err)
|
||||
return &header, nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
@@ -250,9 +262,22 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
|
||||
&ln.Qty1,
|
||||
&ln.Qty2,
|
||||
&ln.Price,
|
||||
&ln.DocCurrencyCode,
|
||||
&ln.RelationCurrencyCode,
|
||||
&ln.PriceExchangeRate,
|
||||
&ln.DocPrice,
|
||||
&ln.DocAmount,
|
||||
&ln.LineDiscount,
|
||||
&ln.TotalDiscount,
|
||||
&ln.TaxBase,
|
||||
&ln.Pct,
|
||||
&ln.VatAmount,
|
||||
&ln.VatDeducation,
|
||||
&ln.NetAmount,
|
||||
&ln.LocalPrice,
|
||||
&ln.LocalAmount,
|
||||
&ln.VatRate,
|
||||
&ln.PCTRate,
|
||||
&ln.DocCurrencyCode,
|
||||
&ln.DeliveryDate,
|
||||
&ln.PlannedDateOfLading,
|
||||
&ln.LineDescription,
|
||||
@@ -267,14 +292,14 @@ func GetOrderByID(orderID string) (*models.OrderHeader, []models.OrderDetail, er
|
||||
&ln.Fit1,
|
||||
&ln.Fit2,
|
||||
); err != nil {
|
||||
return &header, nil, fmt.Errorf("line scan hatası: %w", err)
|
||||
return &header, nil, fmt.Errorf("line scan error: %w", err)
|
||||
}
|
||||
lines = append(lines, ln)
|
||||
}
|
||||
if err := rows.Err(); err != nil {
|
||||
return &header, nil, fmt.Errorf("line rows hatası: %w", err)
|
||||
return &header, nil, fmt.Errorf("line rows error: %w", err)
|
||||
}
|
||||
|
||||
logger.Printf("📦 [GetOrderByID] lines loaded • count=%d", len(lines))
|
||||
logger.Printf("[GetOrderByID] lines loaded count=%d", len(lines))
|
||||
return &header, lines, nil
|
||||
}
|
||||
|
||||
@@ -13,9 +13,10 @@ import (
|
||||
"bssapp-backend/models"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func nf0(v models.NullFloat64) float64 {
|
||||
@@ -25,6 +26,311 @@ func nf0(v models.NullFloat64) float64 {
|
||||
return v.Float64
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// ✅ trOrderLineCurrency UPSERT (Doc Price / Amount yaz)
|
||||
// =======================================================
|
||||
func upsertLineCurrency(
|
||||
tx *sql.Tx,
|
||||
lineID string,
|
||||
|
||||
docCurrency string,
|
||||
exRate float64,
|
||||
|
||||
docPrice float64,
|
||||
qty float64,
|
||||
|
||||
lDis float64, // Line Discount %
|
||||
tDis float64, // Total Discount %
|
||||
|
||||
vatRate float64,
|
||||
ln models.OrderDetail,
|
||||
|
||||
user string,
|
||||
) error {
|
||||
|
||||
// ================= NORMALIZE =================
|
||||
|
||||
if docCurrency == "" {
|
||||
docCurrency = "TRY"
|
||||
}
|
||||
|
||||
if exRate <= 0 {
|
||||
exRate = 1
|
||||
}
|
||||
|
||||
relationCurrency := safeNS(ln.RelationCurrencyCode)
|
||||
if relationCurrency == "" {
|
||||
relationCurrency = docCurrency
|
||||
}
|
||||
|
||||
if ln.LineDiscount.Valid {
|
||||
lDis = ln.LineDiscount.Float64
|
||||
}
|
||||
if ln.TotalDiscount.Valid {
|
||||
tDis = ln.TotalDiscount.Float64
|
||||
}
|
||||
|
||||
basePrice := docPrice // Price (KDV hariç)
|
||||
if docCurrency == "TRY" && ln.LocalPrice.Valid && ln.LocalPrice.Float64 > 0 {
|
||||
basePrice = ln.LocalPrice.Float64
|
||||
}
|
||||
|
||||
amountBase := basePrice * qty // Amount (KDV hariç)
|
||||
if docCurrency == "TRY" && ln.LocalAmount.Valid && ln.LocalAmount.Float64 > 0 {
|
||||
amountBase = ln.LocalAmount.Float64
|
||||
}
|
||||
|
||||
priceVI := basePrice // PriceVI (KDV dahil)
|
||||
if ln.DocPrice.Valid && ln.DocPrice.Float64 > 0 {
|
||||
priceVI = ln.DocPrice.Float64
|
||||
}
|
||||
|
||||
amountVI := priceVI * qty // AmountVI (KDV dahil)
|
||||
if ln.DocAmount.Valid && ln.DocAmount.Float64 > 0 {
|
||||
amountVI = ln.DocAmount.Float64
|
||||
if qty > 0 {
|
||||
priceVI = amountVI / qty
|
||||
}
|
||||
}
|
||||
|
||||
// ================= DISCOUNT =================
|
||||
|
||||
afterLineDis := amountBase * (1 - lDis/100)
|
||||
afterTotalDis := afterLineDis * (1 - tDis/100)
|
||||
|
||||
// ================= TAX =================
|
||||
|
||||
taxBase := afterTotalDis
|
||||
if ln.TaxBase.Valid {
|
||||
taxBase = ln.TaxBase.Float64
|
||||
}
|
||||
|
||||
vat := taxBase * vatRate / 100
|
||||
if ln.VatAmount.Valid {
|
||||
vat = ln.VatAmount.Float64
|
||||
}
|
||||
|
||||
vatDeducation := 0.0
|
||||
if ln.VatDeducation.Valid {
|
||||
vatDeducation = ln.VatDeducation.Float64
|
||||
}
|
||||
|
||||
net := taxBase + vat - vatDeducation
|
||||
if ln.NetAmount.Valid {
|
||||
net = ln.NetAmount.Float64
|
||||
}
|
||||
|
||||
pct := 0.0
|
||||
if ln.Pct.Valid {
|
||||
pct = ln.Pct.Float64
|
||||
}
|
||||
|
||||
// payload yoksa doc tutarı net ile hizala
|
||||
if !ln.DocAmount.Valid {
|
||||
amountVI = net
|
||||
if qty > 0 {
|
||||
priceVI = amountVI / qty
|
||||
}
|
||||
}
|
||||
|
||||
// ================= LOCAL =================
|
||||
|
||||
localPrice := basePrice * exRate
|
||||
localAmount := amountBase * exRate
|
||||
|
||||
localPriceVI := priceVI * exRate
|
||||
localAmountVI := amountVI * exRate
|
||||
|
||||
localTaxBase := taxBase * exRate
|
||||
localVat := vat * exRate
|
||||
localVatDeducation := vatDeducation * exRate
|
||||
localNet := net * exRate
|
||||
|
||||
// ================= DOC =================
|
||||
|
||||
_, err := tx.Exec(`
|
||||
MERGE BAGGI_V3.dbo.trOrderLineCurrency AS T
|
||||
USING (SELECT @p1 AS OrderLineID, @p2 AS CurrencyCode) AS S
|
||||
ON T.OrderLineID=S.OrderLineID AND T.CurrencyCode=S.CurrencyCode
|
||||
|
||||
WHEN MATCHED THEN UPDATE SET
|
||||
RelationCurrencyCode=@p3,
|
||||
|
||||
ExchangeRate=@p4,
|
||||
|
||||
PriceVI=@p5,
|
||||
AmountVI=@p6,
|
||||
|
||||
Price=@p7,
|
||||
Amount=@p8,
|
||||
|
||||
LDiscount1=@p9,
|
||||
TDiscount1=@p10,
|
||||
|
||||
TaxBase=@p11,
|
||||
Pct=@p12,
|
||||
Vat=@p13,
|
||||
VatDeducation=@p14,
|
||||
NetAmount=@p15,
|
||||
|
||||
LastUpdatedUserName=@p16,
|
||||
LastUpdatedDate=GETDATE()
|
||||
|
||||
WHEN NOT MATCHED THEN INSERT (
|
||||
OrderLineID,
|
||||
CurrencyCode,
|
||||
RelationCurrencyCode,
|
||||
|
||||
ExchangeRate,
|
||||
|
||||
PriceVI,
|
||||
AmountVI,
|
||||
|
||||
Price,
|
||||
Amount,
|
||||
|
||||
LDiscount1,
|
||||
TDiscount1,
|
||||
|
||||
TaxBase,
|
||||
Pct,
|
||||
Vat,
|
||||
VatDeducation,
|
||||
NetAmount,
|
||||
|
||||
CreatedUserName,
|
||||
CreatedDate
|
||||
)
|
||||
VALUES (
|
||||
@p1,@p2,@p3,
|
||||
@p4,
|
||||
@p5,@p6,
|
||||
@p7,@p8,
|
||||
@p9,@p10,
|
||||
@p11,@p12,@p13,@p14,@p15,
|
||||
@p16,GETDATE()
|
||||
);`,
|
||||
lineID,
|
||||
docCurrency,
|
||||
relationCurrency,
|
||||
|
||||
exRate,
|
||||
|
||||
priceVI,
|
||||
amountVI,
|
||||
|
||||
basePrice,
|
||||
amountBase,
|
||||
|
||||
lDis,
|
||||
tDis,
|
||||
|
||||
taxBase,
|
||||
pct,
|
||||
vat,
|
||||
vatDeducation,
|
||||
net,
|
||||
|
||||
user,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if docCurrency != "TRY" {
|
||||
_, err = tx.Exec(`
|
||||
MERGE BAGGI_V3.dbo.trOrderLineCurrency AS T
|
||||
USING (SELECT @p1 AS OrderLineID,'TRY' AS CurrencyCode) AS S
|
||||
ON T.OrderLineID=S.OrderLineID AND T.CurrencyCode=S.CurrencyCode
|
||||
|
||||
WHEN MATCHED THEN UPDATE SET
|
||||
RelationCurrencyCode=@p2,
|
||||
|
||||
ExchangeRate=@p3,
|
||||
|
||||
PriceVI=@p4,
|
||||
AmountVI=@p5,
|
||||
|
||||
Price=@p6,
|
||||
Amount=@p7,
|
||||
|
||||
LDiscount1=@p8,
|
||||
TDiscount1=@p9,
|
||||
|
||||
TaxBase=@p10,
|
||||
Pct=@p11,
|
||||
Vat=@p12,
|
||||
VatDeducation=@p13,
|
||||
NetAmount=@p14,
|
||||
|
||||
LastUpdatedUserName=@p15,
|
||||
LastUpdatedDate=GETDATE()
|
||||
|
||||
WHEN NOT MATCHED THEN INSERT (
|
||||
OrderLineID,
|
||||
CurrencyCode,
|
||||
RelationCurrencyCode,
|
||||
|
||||
ExchangeRate,
|
||||
|
||||
PriceVI,
|
||||
AmountVI,
|
||||
|
||||
Price,
|
||||
Amount,
|
||||
|
||||
LDiscount1,
|
||||
TDiscount1,
|
||||
|
||||
TaxBase,
|
||||
Pct,
|
||||
Vat,
|
||||
VatDeducation,
|
||||
NetAmount,
|
||||
|
||||
CreatedUserName,
|
||||
CreatedDate
|
||||
)
|
||||
VALUES (
|
||||
@p1,'TRY',@p2,
|
||||
@p3,
|
||||
@p4,@p5,
|
||||
@p6,@p7,
|
||||
@p8,@p9,
|
||||
@p10,@p11,@p12,@p13,@p14,
|
||||
@p15,GETDATE()
|
||||
);`,
|
||||
lineID,
|
||||
docCurrency,
|
||||
exRate,
|
||||
|
||||
localPriceVI,
|
||||
localAmountVI,
|
||||
|
||||
localPrice,
|
||||
localAmount,
|
||||
|
||||
lDis,
|
||||
tDis,
|
||||
|
||||
localTaxBase,
|
||||
pct,
|
||||
localVat,
|
||||
localVatDeducation,
|
||||
localNet,
|
||||
|
||||
user,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// COMBO KEY & STRING HELPERS
|
||||
// =======================================================
|
||||
@@ -52,6 +358,32 @@ func qtyValue(q models.NullFloat64) float64 {
|
||||
return q.Float64
|
||||
}
|
||||
|
||||
func buildV3AuditUser(user *models.User) string {
|
||||
if user == nil {
|
||||
return "V3-0"
|
||||
}
|
||||
|
||||
v3Name := strings.ToUpper(strings.TrimSpace(user.V3Username))
|
||||
v3Group := user.V3UserGroup
|
||||
if v3Group >= 100 && v3Group%100 == 0 {
|
||||
v3Group = v3Group / 100
|
||||
}
|
||||
|
||||
if v3Name != "" && v3Group > 0 {
|
||||
return fmt.Sprintf("V3-%s%d", v3Name, v3Group)
|
||||
}
|
||||
if v3Name != "" {
|
||||
return "V3-" + v3Name
|
||||
}
|
||||
if username := strings.TrimSpace(user.Username); username != "" {
|
||||
return username
|
||||
}
|
||||
if v3Group > 0 {
|
||||
return fmt.Sprintf("V3-%d", v3Group)
|
||||
}
|
||||
return "V3-0"
|
||||
}
|
||||
|
||||
// VatCode: NullString → string
|
||||
// - NULL → ""
|
||||
// - "0" → "" (FK patlamasın, sadece anlamlı kodlar gönderiyoruz)
|
||||
@@ -173,9 +505,6 @@ func ValidateItemVariant(tx *sql.Tx, ln models.OrderDetail) error {
|
||||
|
||||
}
|
||||
|
||||
// İstersen debug:
|
||||
// fmt.Printf("🧪 VARIANT CHECK item=%q color=%q dim1=%q dim2=%q clientKey=%s\n", item, color, dim1, dim2, safeNS(ln.ClientKey))
|
||||
|
||||
var exists int
|
||||
err := tx.QueryRow(`
|
||||
SELECT CASE WHEN EXISTS (
|
||||
@@ -297,12 +626,6 @@ type OrderLineResult struct {
|
||||
|
||||
// =======================================================
|
||||
// PART 1 — InsertOrder (header + lines insert) — FINAL v5.1
|
||||
// ✔ OrderHeaderID backend üretir
|
||||
// ✔ LOCAL-... numara gelirse gerçek WS numarası üretir
|
||||
// ✔ Full debug
|
||||
// ✔ Tüm satırlar INSERT edilir
|
||||
// ✔ INSERT öncesi ItemVariant Guard
|
||||
// ✔ Payload içi Duplicate Guard (comboKey)
|
||||
// =======================================================
|
||||
|
||||
func InsertOrder(header models.OrderHeader, lines []models.OrderDetail, user *models.User) (string, []OrderLineResult, error) {
|
||||
@@ -317,7 +640,7 @@ func InsertOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
defer tx.Rollback()
|
||||
|
||||
now := time.Now()
|
||||
v3User := fmt.Sprintf("V3U%d-%s", user.V3UserGroup, user.V3Username)
|
||||
v3User := buildV3AuditUser(user)
|
||||
|
||||
// =======================================================
|
||||
// 1) BACKEND — OrderHeaderID üretimi (HER ZAMAN)
|
||||
@@ -368,7 +691,6 @@ func InsertOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 4) HEADER INSERT
|
||||
// =======================================================
|
||||
@@ -423,6 +745,7 @@ VALUES (
|
||||
|
||||
fmt.Println("🟪 HEADER INSERT ÇALIŞIYOR...")
|
||||
|
||||
// ✅ exRate burada gerçekten kullanılıyor (ExchangeRate parametresi)
|
||||
headerParams := []any{
|
||||
header.OrderHeaderID,
|
||||
nullableInt16(header.OrderTypeCode, 1),
|
||||
@@ -474,7 +797,7 @@ VALUES (
|
||||
nullableString(header.GLTypeCode, ""),
|
||||
nullableString(header.DocCurrencyCode, "TRY"),
|
||||
nullableString(header.LocalCurrencyCode, "TRY"),
|
||||
nullableFloat64(header.ExchangeRate, exRate),
|
||||
nullableFloat64(header.ExchangeRate, exRate), // ✅ exRate kullanıldı
|
||||
|
||||
nullableFloat64(header.TDisRate1, 0),
|
||||
nullableFloat64(header.TDisRate2, 0),
|
||||
@@ -520,6 +843,7 @@ VALUES (
|
||||
nullableBool(header.IsProposalBased, false),
|
||||
}
|
||||
|
||||
// ✅ queryHeader artık gerçekten kullanılıyor → "Unused variable 'queryHeader'" biter
|
||||
if _, err := tx.Exec(queryHeader, headerParams...); err != nil {
|
||||
fmt.Println("❌ HEADER INSERT ERROR:", err)
|
||||
return "", nil, fmt.Errorf("header insert hatasi: %w", err)
|
||||
@@ -527,6 +851,7 @@ VALUES (
|
||||
|
||||
fmt.Println("🟩 HEADER INSERT OK — ID:", newID)
|
||||
|
||||
// headerParams ... (senin mevcut hali aynen)
|
||||
// =======================================================
|
||||
// 5) LINE INSERT
|
||||
// =======================================================
|
||||
@@ -590,7 +915,6 @@ VALUES (
|
||||
seenCombo := make(map[string]bool)
|
||||
|
||||
for i, ln := range lines {
|
||||
// ===================== PART 2 (Satır 301-600) =====================
|
||||
fmt.Println("────────────────────────────────────")
|
||||
fmt.Printf("🟨 [INSERT] LINE %d — gelen OrderLineID=%s\n", i+1, ln.OrderLineID)
|
||||
|
||||
@@ -630,6 +954,7 @@ VALUES (
|
||||
}
|
||||
|
||||
planned := nullableDateString(ln.PlannedDateOfLading)
|
||||
|
||||
// ✅ INSERT ÖNCESİ ItemVariant GUARD
|
||||
if qtyValue(ln.Qty1) > 0 {
|
||||
if err := ValidateItemVariant(tx, ln); err != nil {
|
||||
@@ -637,6 +962,7 @@ VALUES (
|
||||
return "", nil, err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(
|
||||
"🚨 INSERT LINE[%d] | LineID=%s ClientKey=%s Item=%q Color=%q Dim1=%q Dim2=%q Dim3=%q Qty1=%v\n",
|
||||
i+1,
|
||||
@@ -708,6 +1034,28 @@ VALUES (
|
||||
return "", nil, fmt.Errorf("line insert hatasi: %w", err)
|
||||
}
|
||||
|
||||
// ✅ NEW: trOrderLineCurrency yaz
|
||||
if err := upsertLineCurrency(
|
||||
tx,
|
||||
ln.OrderLineID,
|
||||
|
||||
safeNS(ln.DocCurrencyCode),
|
||||
nf0(ln.PriceExchangeRate),
|
||||
|
||||
nf0(ln.Price),
|
||||
nf0(ln.Qty1),
|
||||
|
||||
nf0(ln.LDisRate1), // ✅ Line discount
|
||||
0, // ✅ Total discount (istersen header’dan alırsın)
|
||||
|
||||
nf0(ln.VatRate), // ✅ Vat rate
|
||||
ln,
|
||||
|
||||
v3User,
|
||||
); err != nil {
|
||||
return "", nil, fmt.Errorf("currency insert hatası: %w", err)
|
||||
}
|
||||
|
||||
if ln.ClientKey.Valid && ln.ClientKey.String != "" {
|
||||
lineResults = append(lineResults, OrderLineResult{
|
||||
ClientKey: ln.ClientKey.String,
|
||||
@@ -734,25 +1082,16 @@ VALUES (
|
||||
|
||||
// =======================================================
|
||||
// PART 2 — UpdateOrder FULL DEBUG (v4.3)
|
||||
// ✔ ComboKey ile açık satır eşleştirme
|
||||
// ✔ Kapalı satırları korur
|
||||
// ✔ Payload içi Duplicate Guard
|
||||
// ✔ INSERT/UPDATE öncesi ItemVariant Guard (tek noktada)
|
||||
// ✔ Grid’de olmayan açık satırları siler (önce child)
|
||||
// =======================================================
|
||||
|
||||
func UpdateOrder(header models.OrderHeader, lines []models.OrderDetail, user *models.User) ([]OrderLineResult, error) {
|
||||
conn := db.GetDB()
|
||||
|
||||
// ======================================================
|
||||
// 🔍 SCAN DEBUG — HEADER bilgisi
|
||||
// ======================================================
|
||||
fmt.Println("══════════════════════════════════════")
|
||||
fmt.Println("🔍 [DEBUG] UpdateOrder çağrıldı")
|
||||
fmt.Printf("🔍 HeaderID: %v\n", header.OrderHeaderID)
|
||||
fmt.Printf("🔍 Line sayısı: %v\n", len(lines))
|
||||
fmt.Printf("🔍 User: %v (V3: %s/%d)\n",
|
||||
user.Username, user.V3Username, user.V3UserGroup)
|
||||
fmt.Printf("🔍 User: %v (V3: %s/%d)\n", user.Username, user.V3Username, user.V3UserGroup)
|
||||
fmt.Println("══════════════════════════════════════")
|
||||
|
||||
tx, err := conn.Begin()
|
||||
@@ -762,9 +1101,9 @@ func UpdateOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
defer tx.Rollback()
|
||||
|
||||
now := time.Now()
|
||||
v3User := fmt.Sprintf("V3U%d-%s", user.V3UserGroup, user.V3Username)
|
||||
v3User := buildV3AuditUser(user)
|
||||
|
||||
// Döviz kuru
|
||||
// Döviz kuru (Header ExchangeRate fallback)
|
||||
exRate := 1.0
|
||||
if header.DocCurrencyCode.Valid && header.DocCurrencyCode.String != "TRY" {
|
||||
if c, err := GetTodayCurrencyV3(conn, header.DocCurrencyCode.String); err == nil && c.Rate > 0 {
|
||||
@@ -775,11 +1114,16 @@ func UpdateOrder(header models.OrderHeader, lines []models.OrderDetail, user *mo
|
||||
// =======================================================
|
||||
// 0) Mevcut satırları oku (GUID STRING olarak!)
|
||||
// =======================================================
|
||||
|
||||
existingOpen := make(map[string]bool)
|
||||
existingClosed := make(map[string]bool)
|
||||
existingOpenCombo := make(map[string]string)
|
||||
existingClosedCombo := make(map[string]string)
|
||||
existingOpenMeta := make(map[string]struct {
|
||||
item string
|
||||
color string
|
||||
dim1 string
|
||||
dim2 string
|
||||
})
|
||||
|
||||
rows, err := tx.Query(`
|
||||
SELECT
|
||||
@@ -814,16 +1158,41 @@ WHERE OrderHeaderID=@p1
|
||||
}
|
||||
} else {
|
||||
existingOpen[id] = true
|
||||
existingOpenMeta[id] = struct {
|
||||
item string
|
||||
color string
|
||||
dim1 string
|
||||
dim2 string
|
||||
}{
|
||||
item: strings.TrimSpace(item),
|
||||
color: strings.TrimSpace(color),
|
||||
dim1: strings.TrimSpace(dim1),
|
||||
dim2: strings.TrimSpace(dim2),
|
||||
}
|
||||
if combo != "" {
|
||||
existingOpenCombo[combo] = id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isLineInvoiced := func(lineID string) (bool, error) {
|
||||
var exists int
|
||||
err := tx.QueryRow(`
|
||||
SELECT CASE WHEN EXISTS (
|
||||
SELECT 1
|
||||
FROM BAGGI_V3.dbo.trInvoiceLine WITH (NOLOCK)
|
||||
WHERE OrderLineID=@p1
|
||||
) THEN 1 ELSE 0 END
|
||||
`, lineID).Scan(&exists)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("invoice line kontrol query hatasi: %w", err)
|
||||
}
|
||||
return exists == 1, nil
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// HEADER UPDATE
|
||||
// ======================================================
|
||||
|
||||
_, err = tx.Exec(`
|
||||
UPDATE BAGGI_V3.dbo.trOrderHeader SET
|
||||
OrderDate=@p1,
|
||||
@@ -853,11 +1222,12 @@ WHERE OrderHeaderID=@p11
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// ======================================================
|
||||
// PREPARE STATEMENTS
|
||||
// ======================================================
|
||||
|
||||
insStmt, err := tx.Prepare(`INSERT INTO BAGGI_V3.dbo.trOrderLine (
|
||||
insStmt, err := tx.Prepare(`
|
||||
INSERT INTO BAGGI_V3.dbo.trOrderLine (
|
||||
OrderLineID, SortOrder, ItemTypeCode, ItemCode, ColorCode,
|
||||
ItemDim1Code, ItemDim2Code, ItemDim3Code,
|
||||
Qty1, Qty2, CancelQty1, CancelQty2, OrderCancelReasonCode,
|
||||
@@ -872,21 +1242,23 @@ BaseSubCurrAccID, BaseStoreCode,
|
||||
OrderHeaderID, CreatedUserName, CreatedDate,
|
||||
LastUpdatedUserName, LastUpdatedDate,
|
||||
SurplusOrderQtyToleranceRate,
|
||||
WithHoldingTaxTypeCode, DOVCode)
|
||||
WithHoldingTaxTypeCode, DOVCode
|
||||
)
|
||||
VALUES (
|
||||
@p1,@p2,@p3,@p4,@p5,@p6,@p7,@p8,@p9,@p10,
|
||||
@p11,@p12,@p13,@p14,@p15,@p16,@p17,@p18,
|
||||
@p19,@p20,@p21,@p22,@p23,@p24,@p25,@p26,@p27,
|
||||
@p28,@p29,@p30,@p31,@p32,@p33,@p34,@p35,
|
||||
@p36,@p37,@p38,@p39,@p40,@p41,@p42,@p43,
|
||||
@p44,@p45)`)
|
||||
|
||||
@p44,@p45
|
||||
)`)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer insStmt.Close()
|
||||
|
||||
updStmt, err := tx.Prepare(`UPDATE BAGGI_V3.dbo.trOrderLine SET
|
||||
updStmt, err := tx.Prepare(`
|
||||
UPDATE BAGGI_V3.dbo.trOrderLine SET
|
||||
SortOrder=@p1, ItemTypeCode=@p2, ItemCode=@p3, ColorCode=@p4,
|
||||
ItemDim1Code=@p5, ItemDim2Code=@p6, ItemDim3Code=@p7,
|
||||
Qty1=@p8, Qty2=@p9, CancelQty1=@p10, CancelQty2=@p11,
|
||||
@@ -905,17 +1277,18 @@ LastUpdatedUserName=@p37, LastUpdatedDate=@p38,
|
||||
SurplusOrderQtyToleranceRate=@p39,
|
||||
WithHoldingTaxTypeCode=@p40, DOVCode=@p41
|
||||
WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer updStmt.Close()
|
||||
|
||||
// ======================================================
|
||||
// LOOP
|
||||
// ======================================================
|
||||
lineResults := make([]OrderLineResult, 0)
|
||||
seenCombo := make(map[string]bool)
|
||||
|
||||
for _, ln := range lines {
|
||||
|
||||
comboKey := normalizeComboKey(safeNS(ln.ComboKey))
|
||||
if comboKey == "" {
|
||||
comboKey = makeComboKey(ln)
|
||||
@@ -929,7 +1302,7 @@ WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`)
|
||||
seenCombo[comboKey] = true
|
||||
}
|
||||
|
||||
// Kapalı satır
|
||||
// Kapalı satır guard
|
||||
if ln.OrderLineID != "" && existingClosed[ln.OrderLineID] {
|
||||
continue
|
||||
}
|
||||
@@ -941,23 +1314,39 @@ WHERE OrderLineID=@p42 AND ISNULL(IsClosed,0)=0`)
|
||||
|
||||
// DELETE SIGNAL
|
||||
if ln.OrderLineID != "" && qtyValue(ln.Qty1) <= 0 {
|
||||
_, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, ln.OrderLineID)
|
||||
invoiced, err := isLineInvoiced(ln.OrderLineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = tx.Exec(`
|
||||
if invoiced {
|
||||
return nil, &models.ValidationError{
|
||||
Code: "ORDER_LINE_INVOICED",
|
||||
Message: fmt.Sprintf("Faturalanmis satir silinemez (OrderLineID=%s)", ln.OrderLineID),
|
||||
ClientKey: safeNS(ln.ClientKey),
|
||||
ItemCode: strings.TrimSpace(safeNS(ln.ItemCode)),
|
||||
ColorCode: strings.TrimSpace(safeNS(ln.ColorCode)),
|
||||
Dim1: strings.TrimSpace(safeNS(ln.ItemDim1Code)),
|
||||
Dim2: strings.TrimSpace(safeNS(ln.ItemDim2Code)),
|
||||
}
|
||||
}
|
||||
if _, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, ln.OrderLineID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tx.Exec(`
|
||||
DELETE FROM BAGGI_V3.dbo.trOrderLine
|
||||
WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
`, header.OrderHeaderID, ln.OrderLineID)
|
||||
if err != nil {
|
||||
`, header.OrderHeaderID, ln.OrderLineID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(existingOpen, ln.OrderLineID)
|
||||
delete(existingOpenCombo, comboKey)
|
||||
continue
|
||||
}
|
||||
|
||||
isNew := false
|
||||
|
||||
// ID resolve: boşsa combo'dan yakala, yoksa yeni üret
|
||||
if ln.OrderLineID == "" {
|
||||
if dbID, ok := existingOpenCombo[comboKey]; ok {
|
||||
ln.OrderLineID = dbID
|
||||
@@ -967,6 +1356,7 @@ WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
}
|
||||
}
|
||||
|
||||
// Variant guard
|
||||
if qtyValue(ln.Qty1) > 0 {
|
||||
if err := ValidateItemVariant(tx, ln); err != nil {
|
||||
return nil, err
|
||||
@@ -1048,9 +1438,28 @@ WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ Currency UPSERT (insert/update sonrası ortak)
|
||||
if err := upsertLineCurrency(
|
||||
tx,
|
||||
ln.OrderLineID,
|
||||
safeNS(ln.DocCurrencyCode),
|
||||
nf0(ln.PriceExchangeRate),
|
||||
nf0(ln.Price),
|
||||
nf0(ln.Qty1),
|
||||
nf0(ln.LDisRate1),
|
||||
0, // TODO: header TDisRate toplamını istersen buraya bağlarız
|
||||
nf0(ln.VatRate), // satır vat oranı
|
||||
ln,
|
||||
v3User,
|
||||
); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Bu satır işlendi -> existingOpen setinden düş
|
||||
delete(existingOpen, ln.OrderLineID)
|
||||
delete(existingOpenCombo, comboKey)
|
||||
|
||||
// Sonuç mapping
|
||||
if ln.ClientKey.Valid {
|
||||
lineResults = append(lineResults, OrderLineResult{
|
||||
ClientKey: ln.ClientKey.String,
|
||||
@@ -1059,18 +1468,31 @@ WHERE OrderHeaderID=@p1 AND OrderLineID=@p2 AND ISNULL(IsClosed,0)=0
|
||||
}
|
||||
}
|
||||
|
||||
// Grid dışı kalan açık satırlar
|
||||
// =======================================================
|
||||
// Grid dışı kalan açık satırlar (payload'da yok -> sil)
|
||||
// =======================================================
|
||||
for id := range existingOpen {
|
||||
_, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, id)
|
||||
invoiced, err := isLineInvoiced(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLine WHERE OrderLineID=@p1 AND ISNULL(IsClosed,0)=0`, id)
|
||||
if err != nil {
|
||||
if invoiced {
|
||||
meta := existingOpenMeta[id]
|
||||
fmt.Printf("[ORDER_UPDATE] skip delete invoiced line id=%s item=%s color=%s dim1=%s dim2=%s\n",
|
||||
id, meta.item, meta.color, meta.dim1, meta.dim2)
|
||||
continue
|
||||
}
|
||||
if _, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLineCurrency WHERE OrderLineID=@p1`, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := tx.Exec(`DELETE FROM BAGGI_V3.dbo.trOrderLine WHERE OrderLineID=@p1 AND ISNULL(IsClosed,0)=0`, id); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// COMMIT + RETURN
|
||||
// =======================================================
|
||||
if err := tx.Commit(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -10,6 +10,10 @@ import (
|
||||
|
||||
// ========================================================
|
||||
// 📌 GetOrderList — FINAL + CURRENCY SAFE + PIYASA AUTHZ
|
||||
//
|
||||
// ✅ TotalAmount artık trOrderLineCurrency(NetAmount) üzerinden
|
||||
// ve CurrencyCode = Header.DocCurrencyCode satırından gelir.
|
||||
//
|
||||
// ========================================================
|
||||
func GetOrderList(
|
||||
ctx context.Context,
|
||||
@@ -36,10 +40,8 @@ func GetOrderList(
|
||||
}
|
||||
|
||||
if len(codes) == 0 {
|
||||
// hiç yetkisi yok → hiç kayıt dönmesin
|
||||
piyasaWhere = "1=0"
|
||||
} else {
|
||||
// ⚠️ EXISTS içinde kullanılacak
|
||||
piyasaWhere = authz.BuildINClause(
|
||||
"UPPER(f2.CustomerAtt01)",
|
||||
codes,
|
||||
@@ -86,6 +88,29 @@ SELECT
|
||||
ELSE 0
|
||||
END AS TotalAmountUSD,
|
||||
|
||||
ISNULL(l.PackedAmount,0) AS PackedAmount,
|
||||
|
||||
CASE
|
||||
WHEN h.DocCurrencyCode = 'USD'
|
||||
THEN ISNULL(l.PackedAmount,0)
|
||||
|
||||
WHEN h.DocCurrencyCode = 'TRY'
|
||||
AND usd.Rate > 0
|
||||
THEN ISNULL(l.PackedTRY,0) / usd.Rate
|
||||
|
||||
WHEN cur.Rate > 0
|
||||
AND usd.Rate > 0
|
||||
THEN (ISNULL(l.PackedAmount,0) * cur.Rate) / usd.Rate
|
||||
|
||||
ELSE 0
|
||||
END AS PackedUSD,
|
||||
|
||||
CASE
|
||||
WHEN ISNULL(l.TotalAmount,0) > 0
|
||||
THEN (ISNULL(l.PackedAmount,0) * 100.0) / NULLIF(l.TotalAmount,0)
|
||||
ELSE 0
|
||||
END AS PackedRatePct,
|
||||
|
||||
ISNULL(h.IsCreditableConfirmed,0) AS IsCreditableConfirmed,
|
||||
ISNULL(h.Description,'') AS Description,
|
||||
|
||||
@@ -93,12 +118,40 @@ SELECT
|
||||
|
||||
FROM dbo.trOrderHeader h
|
||||
|
||||
-- ✅ TOPLAM ARTIK trOrderLineCurrency'den: CurrencyCode = DocCurrencyCode
|
||||
JOIN (
|
||||
SELECT
|
||||
OrderHeaderID,
|
||||
SUM(Qty1 * Price) AS TotalAmount
|
||||
FROM dbo.trOrderLine
|
||||
GROUP BY OrderHeaderID
|
||||
l.OrderHeaderID,
|
||||
SUM(ISNULL(c.NetAmount,0)) AS TotalAmount,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN ISNULL(c.CurrencyCode,'') = 'TRY'
|
||||
THEN ISNULL(c.NetAmount,0)
|
||||
ELSE 0
|
||||
END
|
||||
) AS TotalTRY,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN ISNULL(l.IsClosed,0) = 1
|
||||
THEN ISNULL(c.NetAmount,0)
|
||||
ELSE 0
|
||||
END
|
||||
) AS PackedAmount,
|
||||
SUM(
|
||||
CASE
|
||||
WHEN ISNULL(l.IsClosed,0) = 1
|
||||
AND ISNULL(c.CurrencyCode,'') = 'TRY'
|
||||
THEN ISNULL(c.NetAmount,0)
|
||||
ELSE 0
|
||||
END
|
||||
) AS PackedTRY
|
||||
FROM dbo.trOrderLine l
|
||||
JOIN dbo.trOrderHeader h2
|
||||
ON h2.OrderHeaderID = l.OrderHeaderID
|
||||
LEFT JOIN dbo.trOrderLineCurrency c
|
||||
ON c.OrderLineID = l.OrderLineID
|
||||
AND c.CurrencyCode = ISNULL(h2.DocCurrencyCode,'TRY')
|
||||
GROUP BY l.OrderHeaderID
|
||||
) l
|
||||
ON l.OrderHeaderID = h.OrderHeaderID
|
||||
|
||||
|
||||
@@ -45,6 +45,8 @@ func (r *MkUserRepository) GetByUsername(username string) (*models.MkUser, error
|
||||
FILTER (WHERE d.code IS NOT NULL),
|
||||
'{}'
|
||||
) AS department_codes,
|
||||
COALESCE(MAX(n.username), '') AS v3_username,
|
||||
COALESCE(MAX(n.user_group_code::text), '') AS v3_usergroup,
|
||||
|
||||
u.password_updated_at,
|
||||
u.created_at,
|
||||
@@ -67,6 +69,13 @@ LEFT JOIN dfusr_dprt ud
|
||||
LEFT JOIN mk_dprt d
|
||||
ON d.id = ud.dprt_id
|
||||
|
||||
LEFT JOIN dfusr_nebim_user un
|
||||
ON un.dfusr_id = u.id
|
||||
|
||||
LEFT JOIN mk_nebim_user n
|
||||
ON n.id = un.mk_nebim_user_id
|
||||
AND n.is_active = true
|
||||
|
||||
WHERE LOWER(u.username) = LOWER($1)
|
||||
|
||||
GROUP BY
|
||||
@@ -85,6 +94,8 @@ LIMIT 1
|
||||
&u.RoleCode,
|
||||
|
||||
pq.Array(&u.DepartmentCodes), // ✅
|
||||
&u.V3Username,
|
||||
&u.V3UserGroup,
|
||||
|
||||
&u.PasswordUpdatedAt,
|
||||
|
||||
@@ -127,6 +138,8 @@ func (r *MkUserRepository) GetByID(id int64) (*models.MkUser, error) {
|
||||
FILTER (WHERE d.code IS NOT NULL),
|
||||
'{}'
|
||||
) AS department_codes,
|
||||
COALESCE(MAX(n.username), '') AS v3_username,
|
||||
COALESCE(MAX(n.user_group_code::text), '') AS v3_usergroup,
|
||||
|
||||
u.password_updated_at,
|
||||
u.created_at,
|
||||
@@ -149,7 +162,14 @@ LEFT JOIN dfusr_dprt ud
|
||||
LEFT JOIN mk_dprt d
|
||||
ON d.id = ud.dprt_id
|
||||
|
||||
WHERE LOWER(u.username) = LOWER($1)
|
||||
LEFT JOIN dfusr_nebim_user un
|
||||
ON un.dfusr_id = u.id
|
||||
|
||||
LEFT JOIN mk_nebim_user n
|
||||
ON n.id = un.mk_nebim_user_id
|
||||
AND n.is_active = true
|
||||
|
||||
WHERE u.id = $1
|
||||
|
||||
GROUP BY
|
||||
u.id, r.id
|
||||
@@ -166,6 +186,8 @@ LIMIT 1
|
||||
&u.RoleID,
|
||||
&u.RoleCode,
|
||||
pq.Array(&u.DepartmentCodes), // ✅
|
||||
&u.V3Username,
|
||||
&u.V3UserGroup,
|
||||
&u.PasswordUpdatedAt,
|
||||
&u.CreatedAt,
|
||||
&u.UpdatedAt,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package routes
|
||||
|
||||
import (
|
||||
"bssapp-backend/models"
|
||||
"bssapp-backend/queries"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
@@ -14,25 +13,50 @@ import (
|
||||
func OrderListExcelRoute(db *sql.DB) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
w.Header().Set("Cache-Control", "no-store")
|
||||
|
||||
// ======================
|
||||
// PARAMS
|
||||
// ======================
|
||||
search := r.URL.Query().Get("search")
|
||||
currAcc := r.URL.Query().Get("CurrAccCode")
|
||||
orderDate := r.URL.Query().Get("OrderDate")
|
||||
|
||||
// ======================
|
||||
// QUERY
|
||||
// ======================
|
||||
rows, err := queries.GetOrderListExcel(db, search, currAcc, orderDate)
|
||||
if err != nil {
|
||||
http.Error(w, "Veritabanı hatası", 500)
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
// ======================
|
||||
// EXCEL INIT
|
||||
// ======================
|
||||
f := excelize.NewFile()
|
||||
sheet := "Orders"
|
||||
f.SetSheetName("Sheet1", sheet)
|
||||
|
||||
// ======================
|
||||
// HEADERS
|
||||
// ======================
|
||||
headers := []string{
|
||||
"Sipariş No", "Tarih", "Cari Kod", "Cari Adı",
|
||||
"Temsilci", "Piyasa", "Onay Tarihi", "PB",
|
||||
"Tutar", "Tutar (USD)", "Açıklama",
|
||||
"Sipariş No",
|
||||
"Tarih",
|
||||
"Cari Kod",
|
||||
"Cari Adı",
|
||||
"Temsilci",
|
||||
"Piyasa",
|
||||
"PB",
|
||||
"Tutar",
|
||||
"Tutar (USD)",
|
||||
"Paketlenen Tutar",
|
||||
"Paketlenen (USD)",
|
||||
"Paketlenme (%)",
|
||||
"USD Kur",
|
||||
"Açıklama",
|
||||
}
|
||||
|
||||
for i, h := range headers {
|
||||
@@ -40,52 +64,108 @@ func OrderListExcelRoute(db *sql.DB) http.Handler {
|
||||
f.SetCellValue(sheet, cell, h)
|
||||
}
|
||||
|
||||
rowIdx := 2
|
||||
// ======================
|
||||
// ROWS
|
||||
// ======================
|
||||
row := 2
|
||||
|
||||
for rows.Next() {
|
||||
var o models.OrderList
|
||||
_ = rows.Scan(
|
||||
&o.OrderHeaderID,
|
||||
&o.OrderNumber,
|
||||
&o.OrderDate,
|
||||
&o.CurrAccCode,
|
||||
&o.CurrAccDescription,
|
||||
&o.MusteriTemsilcisi,
|
||||
&o.Piyasa,
|
||||
&o.CreditableConfirmedDate,
|
||||
&o.DocCurrencyCode,
|
||||
&o.TotalAmount,
|
||||
&o.TotalAmountUSD,
|
||||
&o.IsCreditableConfirmed,
|
||||
&o.Description,
|
||||
&o.ExchangeRateUSD,
|
||||
|
||||
// 🔴 15 KOLON = 15 DEĞİŞKEN
|
||||
var (
|
||||
id, no, date, code, name string
|
||||
rep, piyasa, cur string
|
||||
|
||||
total float64
|
||||
totalUSD float64
|
||||
packedAmount float64
|
||||
packedUSD float64
|
||||
packedRatePct float64
|
||||
usdRate float64
|
||||
|
||||
desc string
|
||||
)
|
||||
|
||||
f.SetSheetRow(sheet, fmt.Sprintf("A%d", rowIdx), &[]interface{}{
|
||||
o.OrderNumber,
|
||||
o.OrderDate,
|
||||
o.CurrAccCode,
|
||||
o.CurrAccDescription,
|
||||
o.MusteriTemsilcisi,
|
||||
o.Piyasa,
|
||||
o.CreditableConfirmedDate,
|
||||
o.DocCurrencyCode,
|
||||
o.TotalAmount,
|
||||
o.TotalAmountUSD,
|
||||
o.Description,
|
||||
// 🔴 SELECT SIRASIYLA BİREBİR
|
||||
err := rows.Scan(
|
||||
&id, // 1
|
||||
&no, // 2
|
||||
&date, // 3
|
||||
&code, // 4
|
||||
&name, // 5
|
||||
&rep, // 6
|
||||
&piyasa, // 7
|
||||
&cur, // 8
|
||||
&total, // 9
|
||||
&totalUSD, // 10
|
||||
&packedAmount, // 11
|
||||
&packedUSD, // 12
|
||||
&packedRatePct, // 13
|
||||
&desc, // 14
|
||||
&usdRate, // 15
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, "Scan error: "+err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
// ======================
|
||||
// WRITE ROW
|
||||
// ======================
|
||||
f.SetSheetRow(sheet, fmt.Sprintf("A%d", row), &[]any{
|
||||
no,
|
||||
date,
|
||||
code,
|
||||
name,
|
||||
rep,
|
||||
piyasa,
|
||||
cur,
|
||||
total,
|
||||
totalUSD,
|
||||
packedAmount,
|
||||
packedUSD,
|
||||
packedRatePct,
|
||||
usdRate,
|
||||
desc,
|
||||
})
|
||||
rowIdx++
|
||||
|
||||
row++
|
||||
}
|
||||
|
||||
// ======================
|
||||
// BUFFER WRITE
|
||||
// ======================
|
||||
buf, err := f.WriteToBuffer()
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 500)
|
||||
return
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf(
|
||||
"order_list_%s.xlsx",
|
||||
time.Now().Format("2006-01-02_15-04"),
|
||||
"siparis_listesi_%s.xlsx",
|
||||
time.Now().Format("20060102_150405"),
|
||||
)
|
||||
|
||||
w.Header().Set("Content-Type",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
w.Header().Set("Content-Disposition",
|
||||
"attachment; filename="+filename)
|
||||
// ======================
|
||||
// RESPONSE
|
||||
// ======================
|
||||
w.Header().Set(
|
||||
"Content-Type",
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
)
|
||||
|
||||
_ = f.Write(w)
|
||||
w.Header().Set(
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\""+filename+"\"",
|
||||
)
|
||||
|
||||
w.Header().Set(
|
||||
"Content-Length",
|
||||
fmt.Sprint(len(buf.Bytes())),
|
||||
)
|
||||
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write(buf.Bytes())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ func OrderListRoute(mssql *sql.DB) http.Handler {
|
||||
count := 0
|
||||
|
||||
// ==================================================
|
||||
// 🧠 SCAN — SQL SELECT ile BİRE BİR (14 kolon)
|
||||
// 🧠 SCAN — SQL SELECT ile BİRE BİR (17 kolon)
|
||||
// ==================================================
|
||||
for rows.Next() {
|
||||
|
||||
@@ -80,11 +80,14 @@ func OrderListRoute(mssql *sql.DB) http.Handler {
|
||||
|
||||
&o.TotalAmount, // 10
|
||||
&o.TotalAmountUSD, // 11
|
||||
&o.PackedAmount, // 12
|
||||
&o.PackedUSD, // 13
|
||||
&o.PackedRatePct, // 14
|
||||
|
||||
&o.IsCreditableConfirmed, // 12
|
||||
&o.Description, // 13
|
||||
&o.IsCreditableConfirmed, // 15
|
||||
&o.Description, // 16
|
||||
|
||||
&o.ExchangeRateUSD, // 14
|
||||
&o.ExchangeRateUSD, // 17
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
|
||||
@@ -3,6 +3,8 @@ package utils
|
||||
import (
|
||||
"bssapp-backend/auth"
|
||||
"bssapp-backend/models"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func UserFromClaims(c *auth.Claims) *models.User {
|
||||
@@ -10,8 +12,20 @@ func UserFromClaims(c *auth.Claims) *models.User {
|
||||
return nil
|
||||
}
|
||||
|
||||
v3Group := 0
|
||||
if raw := strings.TrimSpace(c.V3UserGroup); raw != "" {
|
||||
if parsed, err := strconv.Atoi(raw); err == nil {
|
||||
v3Group = parsed
|
||||
}
|
||||
}
|
||||
|
||||
return &models.User{
|
||||
ID: int(c.ID),
|
||||
Username: c.Username,
|
||||
RoleID: int(c.RoleID),
|
||||
RoleCode: c.RoleCode,
|
||||
V3Username: strings.TrimSpace(c.V3Username),
|
||||
V3UserGroup: v3Group,
|
||||
ForcePasswordChange: c.ForcePasswordChange,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/**
|
||||
* THIS FILE IS GENERATED AUTOMATICALLY.
|
||||
* 1. DO NOT edit this file directly as it won't do anything.
|
||||
* 2. EDIT the original quasar.config file INSTEAD.
|
||||
* 3. DO NOT git commit this file. It should be ignored.
|
||||
*
|
||||
* This file is still here because there was an error in
|
||||
* the original quasar.config file and this allows you to
|
||||
* investigate the Node.js stack error.
|
||||
*
|
||||
* After you fix the original file, this file will be
|
||||
* deleted automatically.
|
||||
**/
|
||||
|
||||
|
||||
// quasar.config.js
|
||||
import { defineConfig } from "@quasar/app-webpack/wrappers";
|
||||
var quasar_config_default = defineConfig(() => {
|
||||
return {
|
||||
// ✅ UYGULAMA KİMLİĞİ (WEB'DE GÖRÜNEN İSİM)
|
||||
productName: "Baggi BSS",
|
||||
productDescription: "Baggi Tekstil Business Support System",
|
||||
// 🔹 Boot dosyaları
|
||||
boot: ["axios", "dayjs"],
|
||||
// 🔹 Global CSS
|
||||
css: ["app.css"],
|
||||
// 🔹 Ekstra icon/font setleri
|
||||
extras: [
|
||||
"roboto-font",
|
||||
"material-icons"
|
||||
],
|
||||
// 🔹 Derleme Ayarları
|
||||
build: {
|
||||
vueRouterMode: "hash",
|
||||
env: {
|
||||
VITE_API_BASE_URL: "http://localhost:8080/api"
|
||||
},
|
||||
esbuildTarget: {
|
||||
browser: ["es2022", "firefox115", "chrome115", "safari14"],
|
||||
node: "node20"
|
||||
}
|
||||
},
|
||||
// 🔹 Geliştirme Sunucusu
|
||||
devServer: {
|
||||
server: { type: "http" },
|
||||
port: 9e3,
|
||||
open: true
|
||||
},
|
||||
// 🔹 Quasar Framework ayarları
|
||||
framework: {
|
||||
config: {
|
||||
notify: { position: "top", timeout: 2500 }
|
||||
},
|
||||
lang: "tr",
|
||||
plugins: ["Loading", "Dialog", "Notify"]
|
||||
},
|
||||
animations: [],
|
||||
ssr: {
|
||||
prodPort: 3e3,
|
||||
middlewares: ["render"],
|
||||
pwa: false
|
||||
},
|
||||
pwa: {
|
||||
workboxMode: "GenerateSW"
|
||||
},
|
||||
capacitor: {
|
||||
hideSplashscreen: true
|
||||
},
|
||||
electron: {
|
||||
preloadScripts: ["electron-preload"],
|
||||
inspectPort: 5858,
|
||||
bundler: "packager",
|
||||
builder: { appId: "baggisowtfaresystem" }
|
||||
},
|
||||
bex: {
|
||||
extraScripts: []
|
||||
}
|
||||
};
|
||||
});
|
||||
export {
|
||||
quasar_config_default as default
|
||||
};
|
||||
@@ -135,13 +135,6 @@ import { Dialog } from 'quasar'
|
||||
|
||||
import { useAuthStore } from 'stores/authStore'
|
||||
import { usePermissionStore } from 'stores/permissionStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
|
||||
/* ================= STORES ================= */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="act-page with-bg">
|
||||
<q-page v-if="canReadUser" class="act-page with-bg">
|
||||
|
||||
<!-- =======================================================
|
||||
🔍 FILTER BAR
|
||||
@@ -83,6 +83,7 @@
|
||||
|
||||
<!-- ✅ YENİ -->
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
outline
|
||||
color="secondary"
|
||||
label="Rol Değişimleri"
|
||||
@@ -206,6 +207,12 @@
|
||||
</q-table>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -217,11 +224,9 @@ import { useActivityLogStore } from 'src/stores/activityLogStore'
|
||||
import { useAuthStore } from 'stores/authStore.js'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canUpdate } = usePermission()
|
||||
const canReadUser = canRead('user')
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
const diffDialog = ref(false)
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="flex flex-center">
|
||||
<q-page v-if="canUpdateSystem" class="flex flex-center">
|
||||
|
||||
<q-card style="width:420px; max-width:90vw">
|
||||
<q-card-section>
|
||||
@@ -45,6 +45,7 @@ class="bg-red-1 text-red q-mt-md"
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
v-if="canUpdateSystem"
|
||||
label="GÜNCELLE"
|
||||
color="primary"
|
||||
:loading="loading"
|
||||
@@ -54,6 +55,11 @@ color="primary"
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
|
||||
</q-page>
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
@@ -64,11 +70,8 @@ import api from 'src/services/api'
|
||||
import { useAuthStore } from 'stores/authStore.js'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canUpdate } = usePermission()
|
||||
const canUpdateSystem = canUpdate('system')
|
||||
|
||||
const $q = useQuasar()
|
||||
const auth = useAuthStore()
|
||||
@@ -117,4 +120,3 @@ async function submit () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
<template>
|
||||
<q-page class="flex flex-center">
|
||||
<q-page v-if="canReadSystem" class="flex flex-center">
|
||||
<p>DashBoard</p>
|
||||
</q-page>
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// buraya JS kodların gelecek
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead } = usePermission()
|
||||
const canReadSystem = canRead('system')
|
||||
</script>
|
||||
|
||||
@@ -59,13 +59,6 @@ import { ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import api from 'src/services/api'
|
||||
import { useAuthStore } from 'stores/authStore.js'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
@@ -117,4 +110,3 @@ async function submit () {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -123,13 +123,6 @@ import { useRouter } from 'vue-router'
|
||||
import { useAuthStore } from 'stores/authStore'
|
||||
import { useQuasar } from 'quasar'
|
||||
import api from 'src/services/api'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
const router = useRouter()
|
||||
const auth = useAuthStore()
|
||||
|
||||
@@ -60,13 +60,6 @@
|
||||
import { ref, computed } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useMePasswordStore } from 'stores/mePasswordStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
const $q = useQuasar()
|
||||
const store = useMePasswordStore()
|
||||
|
||||
@@ -213,7 +213,7 @@
|
||||
<div class="text-subtitle2 text-weight-bold">Sipariş Formu</div>
|
||||
<div>
|
||||
<q-btn
|
||||
v-if="isViewOnly && canReadOrder"
|
||||
v-if="isViewOnly && canExportOrder"
|
||||
label="🖨 SİPARİŞİ YAZDIR"
|
||||
color="primary"
|
||||
icon="print"
|
||||
@@ -766,11 +766,12 @@ import { useAuthStore } from 'src/stores/authStore'
|
||||
import { formatDateInput, formatDateDisplay } from 'src/utils/formatters'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
const { canRead, canWrite, canUpdate, canExport } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const canExportOrder = canExport('order')
|
||||
|
||||
// script setup içinde
|
||||
const formatDate = formatDateDisplay
|
||||
@@ -889,8 +890,8 @@ function hasRowMutationPermission() {
|
||||
}
|
||||
|
||||
function onPrintOrder() {
|
||||
if (!canReadOrder.value) {
|
||||
notifyNoPermission('Siparisi goruntuleme yetkiniz yok')
|
||||
if (!canExportOrder.value) {
|
||||
notifyNoPermission('Siparisi yazdirma yetkiniz yok')
|
||||
return
|
||||
}
|
||||
orderStore.downloadOrderPdf()
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<template>
|
||||
<q-page class="ol-page">
|
||||
<!-- 🔍 Sticky Filter -->
|
||||
<q-page v-if="canReadOrder" class="ol-page">
|
||||
<!-- 🔠Sticky Filter -->
|
||||
<div class="ol-filter-bar">
|
||||
|
||||
<!-- 🔹 TEK SATIR FLEX -->
|
||||
<!-- 🔹 TEK SATIR FLEX -->
|
||||
<div class="ol-filter-row">
|
||||
|
||||
<!-- 🔍 Arama -->
|
||||
<!-- 🔠Arama -->
|
||||
<q-input
|
||||
class="ol-filter-input ol-search"
|
||||
dense
|
||||
@@ -21,7 +21,7 @@
|
||||
</template>
|
||||
</q-input>
|
||||
|
||||
<!-- 🧾 Cari Kodu -->
|
||||
<!-- 🧾 Cari Kodu -->
|
||||
<q-input
|
||||
class="ol-filter-input"
|
||||
dense
|
||||
@@ -31,7 +31,7 @@
|
||||
clearable
|
||||
/>
|
||||
|
||||
<!-- 📅 Sipariş Tarihi -->
|
||||
<!-- 📅 Sipariş Tarihi -->
|
||||
<q-input
|
||||
class="ol-filter-input"
|
||||
dense
|
||||
@@ -41,10 +41,11 @@
|
||||
type="date"
|
||||
/>
|
||||
|
||||
<!-- 🔘 Butonlar -->
|
||||
<!-- 🔘 Butonlar -->
|
||||
<div class="ol-filter-actions">
|
||||
|
||||
<q-btn
|
||||
v-if="canReadOrder"
|
||||
label="Temizle"
|
||||
icon="clear"
|
||||
color="grey-7"
|
||||
@@ -58,6 +59,7 @@
|
||||
</q-btn>
|
||||
|
||||
<q-btn
|
||||
v-if="canReadOrder"
|
||||
label="Yenile"
|
||||
color="primary"
|
||||
icon="refresh"
|
||||
@@ -66,6 +68,7 @@
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
v-if="canExportOrder"
|
||||
label="Excel'e Aktar"
|
||||
icon="download"
|
||||
color="primary"
|
||||
@@ -77,7 +80,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 💰 Toplam -->
|
||||
<!-- 💰 Toplam -->
|
||||
<div class="ol-filter-total">
|
||||
Toplam Görünen Sipariş Tutarı (USD):
|
||||
<strong>
|
||||
@@ -91,7 +94,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 📋 ORDER LIST TABLE -->
|
||||
<!-- 📋 ORDER LIST TABLE -->
|
||||
<q-table
|
||||
title="Mevcut Siparişler"
|
||||
class="ol-table"
|
||||
@@ -103,16 +106,18 @@
|
||||
:rows="store.filteredOrders"
|
||||
:columns="columns"
|
||||
:loading="store.loading"
|
||||
:table-style="tableStyle"
|
||||
no-data-label="Sipariş bulunamadı"
|
||||
:rows-per-page-options="[0]"
|
||||
hide-bottom
|
||||
>
|
||||
|
||||
<!-- 📄 PDF + DURUM -->
|
||||
<!-- 📄 PDF + DURUM -->
|
||||
<template #body-cell-IsCreditableConfirmed="props">
|
||||
<q-td :props="props" class="text-center q-gutter-sm">
|
||||
|
||||
<q-btn
|
||||
v-if="canExportOrder"
|
||||
icon="picture_as_pdf"
|
||||
color="red"
|
||||
flat
|
||||
@@ -136,7 +141,7 @@
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<!-- 📅 Tarih -->
|
||||
<!-- 📅 Tarih -->
|
||||
<template #body-cell-OrderDate="props">
|
||||
<q-td :props="props" class="text-center">
|
||||
{{ formatDate(props.row.OrderDate) }}
|
||||
@@ -149,7 +154,7 @@
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<!-- 🧾 Cari Adı — 2 Satır -->
|
||||
<!-- 🧾 Cari Adı — 2 Satır -->
|
||||
<template #body-cell-CurrAccDescription="props">
|
||||
<q-td :props="props" class="ol-col-cari ol-col-multiline">
|
||||
{{ props.value }}
|
||||
@@ -159,7 +164,7 @@
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<!-- 📝 Açıklama — 5 Satır -->
|
||||
<!-- 📠Açıklama — 5 Satır -->
|
||||
<template #body-cell-Description="props">
|
||||
<q-td :props="props" class="ol-col-desc ol-col-multiline">
|
||||
{{ props.value }}
|
||||
@@ -169,10 +174,11 @@
|
||||
</q-td>
|
||||
</template>
|
||||
|
||||
<!-- 🔗 Aç -->
|
||||
<!-- 🔗 aç -->
|
||||
<template #body-cell-select="props">
|
||||
<q-td :props="props" class="text-center">
|
||||
<q-btn
|
||||
v-if="canUpdateOrder"
|
||||
icon="open_in_new"
|
||||
color="primary"
|
||||
flat
|
||||
@@ -180,7 +186,7 @@
|
||||
dense
|
||||
@click="selectOrder(props.row)"
|
||||
>
|
||||
<q-tooltip>Siparişi Aç</q-tooltip>
|
||||
<q-tooltip>Siparişi aç</q-tooltip>
|
||||
</q-btn>
|
||||
</q-td>
|
||||
</template>
|
||||
@@ -193,10 +199,16 @@
|
||||
</q-banner>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, watch } from 'vue'
|
||||
import { computed, onMounted, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useQuasar } from 'quasar'
|
||||
|
||||
@@ -204,11 +216,12 @@ import { useOrderListStore } from 'src/stores/OrdernewListStore'
|
||||
import { useAuthStore } from 'src/stores/authStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
const { canRead, canWrite, canUpdate, canExport } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const canExportOrder = canExport('order')
|
||||
|
||||
/* =========================
|
||||
INIT
|
||||
@@ -217,9 +230,13 @@ const canUpdateOrder = canUpdate('order')
|
||||
const router = useRouter()
|
||||
const $q = useQuasar()
|
||||
|
||||
// ⚠️ ÖNCE store tanımlanır
|
||||
// âš ï¸ Ã–NCE store tanımlanır
|
||||
const store = useOrderListStore()
|
||||
|
||||
const tableStyle = computed(() => ({
|
||||
minWidth: $q.screen.lt.lg ? '1280px' : '100%'
|
||||
}))
|
||||
|
||||
/* =========================
|
||||
SEARCH DEBOUNCE
|
||||
========================= */
|
||||
@@ -241,14 +258,15 @@ watch(
|
||||
HELPERS
|
||||
========================= */
|
||||
function exportExcel () {
|
||||
if (!canExportOrder.value) {
|
||||
$q.notify({ type: 'negative', message: 'Excel export yetkiniz yok', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
const auth = useAuthStore()
|
||||
|
||||
if (!auth?.token) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Oturum bulunamadı',
|
||||
position: 'top-right'
|
||||
})
|
||||
$q.notify({ type: 'negative', message: 'Oturum bulunamadı', position: 'top-right' })
|
||||
return
|
||||
}
|
||||
|
||||
@@ -261,19 +279,43 @@ function exportExcel () {
|
||||
const url = `http://localhost:8080/api/orders/export?${params.toString()}`
|
||||
|
||||
fetch(url, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${auth.token}`
|
||||
}
|
||||
method: 'GET',
|
||||
headers: { Authorization: `Bearer ${auth.token}` }
|
||||
})
|
||||
.then(async res => {
|
||||
// ✅ 200 değilse EXCEL İNDİRME
|
||||
if (!res.ok) {
|
||||
const text = await res.text()
|
||||
throw new Error(`HTTP ${res.status} - ${text}`)
|
||||
}
|
||||
|
||||
// ✅ Content-Type kontrol (debug için)
|
||||
const ct = res.headers.get('content-type') || ''
|
||||
if (!ct.includes('spreadsheetml.sheet')) {
|
||||
const text = await res.text()
|
||||
throw new Error(`Beklenmeyen Content-Type: ${ct} | Body: ${text}`)
|
||||
}
|
||||
|
||||
return res.blob()
|
||||
})
|
||||
.then(res => res.blob())
|
||||
.then(blob => {
|
||||
const link = document.createElement('a')
|
||||
link.href = URL.createObjectURL(blob)
|
||||
link.download = 'siparis_listesi.xlsx'
|
||||
link.click()
|
||||
})
|
||||
.catch(err => {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Excel export hatası: ' + (err?.message || err),
|
||||
position: 'top-right',
|
||||
timeout: 8000
|
||||
})
|
||||
console.error(err)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function formatDate (s) {
|
||||
if (!s) return ''
|
||||
const [y, m, d] = s.split('-')
|
||||
@@ -285,12 +327,18 @@ function formatDate (s) {
|
||||
========================= */
|
||||
|
||||
const columns = [
|
||||
{ name: 'select', label: '', field: 'select', align: 'center', sortable: false },
|
||||
|
||||
{ name: 'OrderNumber', label: 'Sipariş No', field: 'OrderNumber', align: 'left', sortable: true },
|
||||
{ name: 'OrderDate', label: 'Tarih', field: 'OrderDate', align: 'center', sortable: true },
|
||||
|
||||
{ name: 'CurrAccCode', label: 'Cari Kod', field: 'CurrAccCode', align: 'left', sortable: true },
|
||||
{
|
||||
name: 'select',
|
||||
label: '',
|
||||
field: 'select',
|
||||
align: 'center',
|
||||
sortable: false,
|
||||
style: 'width:42px; min-width:42px; max-width:42px; padding:2px 4px;',
|
||||
headerStyle: 'width:42px; min-width:42px; max-width:42px; padding:2px 4px;'
|
||||
},
|
||||
{ name: 'OrderNumber', label: 'Sipariş No', field: 'OrderNumber', align: 'left', sortable: true, style: 'width:108px; min-width:108px;', headerStyle: 'width:108px; min-width:108px;' },
|
||||
{ name: 'OrderDate', label: 'Tarih', field: 'OrderDate', align: 'center', sortable: true, style: 'width:78px; min-width:78px;', headerStyle: 'width:78px; min-width:78px;' },
|
||||
{ name: 'CurrAccCode', label: 'Cari Kod', field: 'CurrAccCode', align: 'left', sortable: true, style: 'width:78px; min-width:78px;', headerStyle: 'width:78px; min-width:78px;' },
|
||||
|
||||
{
|
||||
name: 'CurrAccDescription',
|
||||
@@ -300,14 +348,13 @@ const columns = [
|
||||
sortable: true,
|
||||
classes: 'ol-col-cari',
|
||||
headerClasses: 'ol-col-cari',
|
||||
style: 'max-width:200px'
|
||||
style: 'width:130px; min-width:130px; max-width:130px;',
|
||||
headerStyle: 'width:130px; min-width:130px; max-width:130px;'
|
||||
},
|
||||
|
||||
{ name: 'MusteriTemsilcisi', label: 'Temsilci', field: 'MusteriTemsilcisi', align: 'left', sortable: true },
|
||||
{ name: 'Piyasa', label: 'Piyasa', field: 'Piyasa', align: 'left', sortable: true },
|
||||
|
||||
{ name: 'CreditableConfirmedDate', label: 'Onay', field: 'CreditableConfirmedDate', align: 'center', sortable: true },
|
||||
{ name: 'DocCurrencyCode', label: 'PB', field: 'DocCurrencyCode', align: 'center', sortable: true },
|
||||
{ name: 'MusteriTemsilcisi', label: 'Temsilci', field: 'MusteriTemsilcisi', align: 'left', sortable: true, style: 'width:78px; min-width:78px;', headerStyle: 'width:78px; min-width:78px;' },
|
||||
{ name: 'Piyasa', label: 'Piyasa', field: 'Piyasa', align: 'left', sortable: true, style: 'width:74px; min-width:74px;', headerStyle: 'width:74px; min-width:74px;' },
|
||||
{ name: 'CreditableConfirmedDate', label: 'Onay', field: 'CreditableConfirmedDate', align: 'center', sortable: true, style: 'width:82px; min-width:82px;', headerStyle: 'width:82px; min-width:82px;' },
|
||||
{ name: 'DocCurrencyCode', label: 'PB', field: 'DocCurrencyCode', align: 'center', sortable: true, style: 'width:46px; min-width:46px;', headerStyle: 'width:46px; min-width:46px;' },
|
||||
|
||||
{
|
||||
name: 'TotalAmount',
|
||||
@@ -315,6 +362,8 @@ const columns = [
|
||||
field: 'TotalAmount',
|
||||
align: 'right',
|
||||
sortable: true,
|
||||
style: 'width:94px; min-width:94px;',
|
||||
headerStyle: 'width:94px; min-width:94px;',
|
||||
format: (val, row) =>
|
||||
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) +
|
||||
' ' + row.DocCurrencyCode
|
||||
@@ -326,11 +375,49 @@ const columns = [
|
||||
field: 'TotalAmountUSD',
|
||||
align: 'right',
|
||||
sortable: true,
|
||||
style: 'width:96px; min-width:96px;',
|
||||
headerStyle: 'width:96px; min-width:96px;',
|
||||
format: val =>
|
||||
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
|
||||
},
|
||||
|
||||
{ name: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true },
|
||||
{
|
||||
name: 'PackedAmount',
|
||||
label: 'Paket Tutar',
|
||||
field: 'PackedAmount',
|
||||
align: 'right',
|
||||
sortable: true,
|
||||
style: 'width:98px; min-width:98px;',
|
||||
headerStyle: 'width:98px; min-width:98px;',
|
||||
format: (val, row) =>
|
||||
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) +
|
||||
' ' + (row.DocCurrencyCode || '')
|
||||
},
|
||||
|
||||
{
|
||||
name: 'PackedUSD',
|
||||
label: 'Paket USD',
|
||||
field: 'PackedUSD',
|
||||
align: 'right',
|
||||
sortable: true,
|
||||
style: 'width:96px; min-width:96px;',
|
||||
headerStyle: 'width:96px; min-width:96px;',
|
||||
format: val =>
|
||||
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' USD'
|
||||
},
|
||||
|
||||
{
|
||||
name: 'PackedRatePct',
|
||||
label: 'Paket %',
|
||||
field: 'PackedRatePct',
|
||||
align: 'right',
|
||||
sortable: true,
|
||||
style: 'width:72px; min-width:72px;',
|
||||
headerStyle: 'width:72px; min-width:72px;',
|
||||
format: val =>
|
||||
Number(val || 0).toLocaleString('tr-TR', { minimumFractionDigits: 2 }) + ' %'
|
||||
},
|
||||
{ name: 'IsCreditableConfirmed', label: 'Durum', field: 'IsCreditableConfirmed', align: 'center', sortable: true, style: 'width:66px; min-width:66px;', headerStyle: 'width:66px; min-width:66px;' },
|
||||
|
||||
{
|
||||
name: 'Description',
|
||||
@@ -340,10 +427,10 @@ const columns = [
|
||||
sortable: false,
|
||||
classes: 'ol-col-desc',
|
||||
headerClasses: 'ol-col-desc',
|
||||
style: 'max-width:220px'
|
||||
style: 'width:130px; min-width:130px; max-width:130px;',
|
||||
headerStyle: 'width:130px; min-width:130px; max-width:130px;'
|
||||
},
|
||||
|
||||
{ name: 'pdf', label: 'PDF', field: 'pdf', align: 'center', sortable: false }
|
||||
{ name: 'pdf', label: 'PDF', field: 'pdf', align: 'center', sortable: false, style: 'width:44px; min-width:44px;', headerStyle: 'width:44px; min-width:44px;' }
|
||||
]
|
||||
|
||||
/* =========================
|
||||
@@ -351,6 +438,11 @@ const columns = [
|
||||
========================= */
|
||||
|
||||
function selectOrder (row) {
|
||||
if (!canUpdateOrder.value) {
|
||||
$q.notify({ type: 'negative', message: 'Siparis guncelleme yetkiniz yok' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!row?.OrderHeaderID) {
|
||||
$q.notify({ type: 'warning', message: 'OrderHeaderID bulunamadı' })
|
||||
return
|
||||
@@ -364,6 +456,11 @@ function selectOrder (row) {
|
||||
}
|
||||
|
||||
async function printPDF (row) {
|
||||
if (!canExportOrder.value) {
|
||||
$q.notify({ type: 'negative', message: 'PDF export yetkiniz yok' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!row?.OrderHeaderID) return
|
||||
|
||||
const token = useAuthStore().token
|
||||
@@ -402,7 +499,48 @@ function clearFilters () {
|
||||
========================= */
|
||||
|
||||
onMounted(() => {
|
||||
if (canReadOrder.value) {
|
||||
store.fetchOrders()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
.ol-page {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.ol-table :deep(.q-table thead th) {
|
||||
font-size: 11px;
|
||||
line-height: 1.1;
|
||||
font-weight: 700;
|
||||
padding: 3px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ol-table :deep(.q-table tbody td) {
|
||||
font-size: 11px;
|
||||
line-height: 1.15;
|
||||
padding: 2px 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ol-col-multiline {
|
||||
white-space: normal !important;
|
||||
word-break: break-word;
|
||||
line-height: 1.1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (max-width: 1600px) {
|
||||
.ol-table :deep(.q-table thead th),
|
||||
.ol-table :deep(.q-table tbody td) {
|
||||
font-size: 10px;
|
||||
padding: 2px 4px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1 +1,122 @@
|
||||
<template></template>
|
||||
<template>
|
||||
<q-page
|
||||
v-if="canExportOrder"
|
||||
class="q-pa-md"
|
||||
>
|
||||
<q-card flat bordered class="q-pa-md" style="max-width: 720px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Order PDF</div>
|
||||
<div class="text-caption text-grey-7">Order ID: {{ orderId || '-' }}</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions align="right" class="q-gutter-sm">
|
||||
<q-btn
|
||||
v-if="canOpenPdf"
|
||||
color="primary"
|
||||
icon="picture_as_pdf"
|
||||
label="PDF Ac"
|
||||
:loading="loading"
|
||||
:disable="loading || !orderId"
|
||||
@click="openPdf"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
v-if="canEditOrder"
|
||||
flat
|
||||
color="secondary"
|
||||
label="Siparise Don"
|
||||
:disable="!orderId"
|
||||
@click="goToOrder"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-page>
|
||||
|
||||
<q-page
|
||||
v-else
|
||||
class="q-pa-md flex flex-center"
|
||||
>
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
import { useOrderEntryStore } from 'src/stores/orderentryStore'
|
||||
|
||||
const { canWrite, canUpdate, canExport } = usePermission()
|
||||
|
||||
const canExportOrder = canExport('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
const canEditOrder = computed(() => canWriteOrder.value || canUpdateOrder.value)
|
||||
const canOpenPdf = computed(() => canExportOrder.value)
|
||||
|
||||
const $q = useQuasar()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const orderStore = useOrderEntryStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const orderId = computed(() => String(route.params.id || '').trim())
|
||||
|
||||
async function openPdf () {
|
||||
if (!canOpenPdf.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Siparis PDF export yetkiniz yok'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!orderId.value) {
|
||||
$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Order ID bulunamadi'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
loading.value = true
|
||||
try {
|
||||
await orderStore.downloadOrderPdf(orderId.value)
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err?.message || 'PDF acilamadi'
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function goToOrder () {
|
||||
if (!canEditOrder.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Siparis duzenleme yetkiniz yok'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!orderId.value) {
|
||||
$q.notify({
|
||||
type: 'warning',
|
||||
message: 'Order ID bulunamadi'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const routeName = canUpdateOrder.value ? 'order-edit' : 'order-entry'
|
||||
router.push({
|
||||
name: routeName,
|
||||
params: { orderHeaderID: orderId.value }
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page padding>
|
||||
<q-page v-if="canReadSystem" padding>
|
||||
|
||||
<div class="text-h6 q-mb-md">
|
||||
Rol + Departman Yetkilendirme
|
||||
@@ -76,6 +76,7 @@
|
||||
<div class="q-mt-md">
|
||||
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
color="primary"
|
||||
icon="save"
|
||||
label="Kaydet"
|
||||
@@ -86,6 +87,12 @@
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -96,11 +103,9 @@ import { Notify } from 'quasar'
|
||||
import api from 'src/services/api'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canUpdate } = usePermission()
|
||||
const canReadSystem = canRead('system')
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
|
||||
/* ================= STATE ================= */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="workorder-page with-bg">
|
||||
<q-page v-if="canReadOrder" class="workorder-page with-bg">
|
||||
|
||||
<!-- ===============================
|
||||
🔹 ÜST: İŞ EMRİ BİLGİLERİ
|
||||
@@ -75,7 +75,7 @@
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
v-if="isCreateMode"
|
||||
v-if="isCreateMode && canWriteOrder"
|
||||
label="Ana Görsel Seç"
|
||||
icon="image"
|
||||
class="q-mt-sm"
|
||||
@@ -113,7 +113,7 @@
|
||||
</q-card>
|
||||
</div>
|
||||
|
||||
<div v-if="isCreateMode" class="col-12">
|
||||
<div v-if="isCreateMode && canWriteOrder" class="col-12">
|
||||
<q-btn
|
||||
label="Detay Görsel Ekle"
|
||||
icon="add"
|
||||
@@ -143,7 +143,7 @@
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
v-if="isCreateMode"
|
||||
v-if="isCreateMode && canWriteOrder"
|
||||
label="Talimat Görseli Ekle"
|
||||
icon="add"
|
||||
flat
|
||||
@@ -161,7 +161,7 @@
|
||||
=============================== -->
|
||||
<div class="row justify-end q-mt-md">
|
||||
<q-btn
|
||||
v-if="isCreateMode"
|
||||
v-if="isCreateMode && canWriteOrder"
|
||||
label="Kaydet"
|
||||
color="positive"
|
||||
icon="save"
|
||||
@@ -169,7 +169,7 @@
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
v-if="isViewMode"
|
||||
v-if="isViewMode && canExportOrder"
|
||||
label="PDF"
|
||||
color="primary"
|
||||
icon="picture_as_pdf"
|
||||
@@ -178,17 +178,23 @@
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
const { canRead, canWrite, canExport } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const canExportOrder = canExport('order')
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
@@ -209,11 +215,15 @@ const detailImages = ref([])
|
||||
const instructionImages = ref([])
|
||||
|
||||
function openImagePicker(type) {
|
||||
if (!canWriteOrder.value) return
|
||||
|
||||
// Şimdilik stub
|
||||
console.log('Image picker:', type)
|
||||
}
|
||||
|
||||
function saveWorkOrder() {
|
||||
if (!canWriteOrder.value) return
|
||||
|
||||
console.log('SAVE', {
|
||||
form: form.value,
|
||||
mainImage: mainImage.value,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
<q-page v-if="canReadOrder" class="q-pa-md">
|
||||
|
||||
<!-- =====================================================
|
||||
🔹 BAŞLIK + AKSİYONLAR
|
||||
@@ -10,6 +10,7 @@
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
v-if="canWriteOrder"
|
||||
color="primary"
|
||||
icon="add"
|
||||
label="Yeni İş Emri"
|
||||
@@ -83,6 +84,12 @@
|
||||
</q-table>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -90,11 +97,10 @@ import { ref, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
const { canRead, canWrite } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
@@ -192,10 +198,12 @@ const filteredRows = computed(() => {
|
||||
// 🔹 AKSİYONLAR
|
||||
// =====================================================
|
||||
function goNew () {
|
||||
if (!canWriteOrder.value) return
|
||||
router.push('/app/production-work-orders/new')
|
||||
}
|
||||
|
||||
function goView (evt, row) {
|
||||
if (!canReadOrder.value) return
|
||||
router.push(`/app/production-work-orders/view/${row.id}`)
|
||||
}
|
||||
|
||||
|
||||
@@ -125,13 +125,6 @@ import { useQuasar } from 'quasar'
|
||||
|
||||
import api, { post } from 'src/services/api'
|
||||
import { useAuthStore } from 'stores/authStore.js'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
|
||||
/* -------------------------------------------------- */
|
||||
/* INIT */
|
||||
@@ -261,4 +254,3 @@ async function submit () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div v-if="!lookupsLoaded" class="q-pa-xl flex flex-center">
|
||||
<div v-if="canUpdateUser && !lookupsLoaded" class="q-pa-xl flex flex-center">
|
||||
|
||||
<q-spinner
|
||||
color="primary"
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<q-page class="permissions-page">
|
||||
<q-page v-if="canUpdateUser" class="permissions-page">
|
||||
|
||||
<!-- ================= STICKY STACK ================= -->
|
||||
<div class="sticky-stack">
|
||||
@@ -69,6 +69,7 @@
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
color="primary"
|
||||
icon="save"
|
||||
label="Kaydet"
|
||||
@@ -165,6 +166,12 @@
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -175,11 +182,8 @@ import { Notify } from 'quasar'
|
||||
import api from 'src/services/api'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canUpdate } = usePermission()
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
|
||||
/* ================= STATE ================= */
|
||||
@@ -432,4 +436,3 @@ watch(deptCode, v => console.log('DEPT >>>', v))
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<!-- src/pages/StatementHeaderReport.vue -->
|
||||
<template>
|
||||
<q-page class="q-pa-md page-col">
|
||||
<q-page v-if="canReadFinance" class="q-pa-md page-col">
|
||||
<!-- Başlık ve PDF butonu -->
|
||||
<div class="row justify-between items-center q-mb-md">
|
||||
<div class="text-h6">📄 Cari Hesap Raporu</div>
|
||||
<q-btn
|
||||
v-if="canExportFinance"
|
||||
color="red"
|
||||
icon="picture_as_pdf"
|
||||
label="PDF Yazdır"
|
||||
@@ -44,6 +45,12 @@
|
||||
</div>
|
||||
</q-card>
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -53,11 +60,9 @@ import { useDownloadstHeadStore } from 'src/stores/downloadstHeadStore'
|
||||
import dayjs from 'dayjs'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canExport } = usePermission()
|
||||
const canReadFinance = canRead('finance')
|
||||
const canExportFinance = canExport('finance')
|
||||
|
||||
const $q = useQuasar()
|
||||
const downloadstHeadStore = useDownloadstHeadStore()
|
||||
@@ -76,6 +81,15 @@ const selectedMonType = ref(monetaryTypeOptions[0].value)
|
||||
|
||||
// indirme butonu
|
||||
async function handlestHeadDownload() {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'PDF export yetkiniz yok',
|
||||
position: 'top-right'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("▶️ [DEBUG] handlestHeadDownload:", accountCode.value, startDate.value, endDate.value, selectedMonType.value)
|
||||
|
||||
if (!accountCode.value || !startDate.value || !endDate.value) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md page-col">
|
||||
<q-page v-if="canReadFinance" class="q-pa-md page-col">
|
||||
|
||||
<!-- 🔹 Cari Kod / İsim (sabit) -->
|
||||
<div class="filter-sticky">
|
||||
@@ -135,6 +135,7 @@
|
||||
|
||||
<!-- ✅ PDF Yazdır Dropdown -->
|
||||
<q-btn-dropdown
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="red"
|
||||
icon="picture_as_pdf"
|
||||
@@ -258,6 +259,12 @@
|
||||
</q-table>
|
||||
</div>
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -270,11 +277,9 @@ import { useDownloadstpdfStore } from 'src/stores/downloadstpdfStore'
|
||||
import dayjs from 'dayjs'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canExport } = usePermission()
|
||||
const canReadFinance = canRead('finance')
|
||||
const canExportFinance = canExport('finance')
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
@@ -436,6 +441,15 @@ function toggleLeftCols() {
|
||||
|
||||
/* 🔹 PDF İndirme Butonuna bağla */
|
||||
async function handleDownload() {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'PDF export yetkiniz yok',
|
||||
position: 'top-right'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("▶️ [DEBUG] handleDownload:", selectedCari.value, dateFrom.value, dateTo.value)
|
||||
|
||||
if (!selectedCari.value || !dateFrom.value || !dateTo.value) {
|
||||
@@ -468,6 +482,15 @@ import { useDownloadstHeadStore } from 'src/stores/downloadstHeadStore'
|
||||
const downloadstHeadStore = useDownloadstHeadStore()
|
||||
|
||||
async function CurrheadDownload() {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'PDF export yetkiniz yok',
|
||||
position: 'top-right'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("▶️ [DEBUG] CurrheadDownload:", selectedCari.value, dateFrom.value, dateTo.value)
|
||||
|
||||
if (!selectedCari.value || !dateFrom.value || !dateTo.value) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md">
|
||||
|
||||
<q-page
|
||||
v-if="canSendTestMail"
|
||||
class="q-pa-md"
|
||||
>
|
||||
<q-card flat bordered class="q-pa-md" style="max-width: 500px">
|
||||
<q-card-section>
|
||||
<div class="text-h6">SMTP Test Mail</div>
|
||||
@@ -9,7 +11,7 @@
|
||||
<q-card-section>
|
||||
<q-input
|
||||
v-model="to"
|
||||
label="Gönderilecek mail"
|
||||
label="Gonderilecek mail"
|
||||
filled
|
||||
dense
|
||||
/>
|
||||
@@ -17,28 +19,36 @@
|
||||
|
||||
<q-card-actions align="right">
|
||||
<q-btn
|
||||
v-if="canSendTestMail"
|
||||
color="primary"
|
||||
label="Test Mail Gönder"
|
||||
label="Test Mail Gonder"
|
||||
:loading="store.loading"
|
||||
:disable="!canSendTestMail"
|
||||
@click="send"
|
||||
/>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</q-page>
|
||||
|
||||
<q-page
|
||||
v-else
|
||||
class="q-pa-md flex flex-center"
|
||||
>
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useQuasar } from 'quasar'
|
||||
import { useMailTestStore } from 'src/stores/mailTestStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canWrite } = usePermission()
|
||||
const canWriteUser = canWrite('user')
|
||||
const canSendTestMail = computed(() => canWriteUser.value)
|
||||
|
||||
const $q = useQuasar()
|
||||
const store = useMailTestStore()
|
||||
@@ -46,17 +56,25 @@ const store = useMailTestStore()
|
||||
const to = ref('mehmet.kececi@baggi.com.tr')
|
||||
|
||||
async function send () {
|
||||
if (!canSendTestMail.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'Test mail gonderme yetkiniz yok'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await store.sendTestMail(to.value)
|
||||
|
||||
$q.notify({
|
||||
type: 'positive',
|
||||
message: 'Test mail gönderildi'
|
||||
message: 'Test mail gonderildi'
|
||||
})
|
||||
} catch (err) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: err?.message || 'Mail gönderilemedi'
|
||||
message: err?.message || 'Mail gonderilemedi'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="user-detail-page">
|
||||
<q-page v-if="canAccessPage" class="user-detail-page">
|
||||
|
||||
<!-- LOADING -->
|
||||
<q-inner-loading :showing="loading">
|
||||
@@ -39,6 +39,7 @@
|
||||
|
||||
<div>
|
||||
<q-btn
|
||||
v-if="canSaveUser"
|
||||
:label="saveLabel"
|
||||
color="primary"
|
||||
icon="save"
|
||||
@@ -46,6 +47,7 @@
|
||||
@click="onSave"
|
||||
/>
|
||||
<q-btn
|
||||
v-if="canReadUser"
|
||||
label="LİSTEYE DÖN"
|
||||
flat
|
||||
icon="arrow_back"
|
||||
@@ -80,6 +82,7 @@
|
||||
|
||||
<div class="row items-center">
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
label="PAROLA MAİLİ GÖNDER"
|
||||
color="primary"
|
||||
icon="mail"
|
||||
@@ -240,6 +243,12 @@
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -251,10 +260,9 @@ import { useUserDetailStore } from 'src/stores/UserDetailStore'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const canReadUser = canRead('user')
|
||||
const canWriteUser = canWrite('user')
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
const $q = useQuasar()
|
||||
const route = useRoute()
|
||||
@@ -280,6 +288,12 @@ const mode = computed(() => route.meta.mode || 'edit')
|
||||
const isNew = computed(() => mode.value === 'new')
|
||||
const isEdit = computed(() => mode.value === 'edit')
|
||||
const isView = computed(() => mode.value === 'view')
|
||||
const canAccessPage = computed(() => {
|
||||
if (isNew.value) return canWriteUser.value
|
||||
if (isEdit.value) return canUpdateUser.value
|
||||
return canReadUser.value
|
||||
})
|
||||
const canSaveUser = computed(() => isNew.value ? canWriteUser.value : canUpdateUser.value)
|
||||
|
||||
const userId = computed(() => (isEdit.value || isView.value) ? Number(route.params.id) : null)
|
||||
|
||||
@@ -306,6 +320,8 @@ const canSendPasswordMail = computed(() => {
|
||||
watch(
|
||||
() => userId.value,
|
||||
async (id) => {
|
||||
if (!canAccessPage.value) return
|
||||
|
||||
await store.fetchLookups()
|
||||
|
||||
if (!id) {
|
||||
@@ -320,6 +336,11 @@ watch(
|
||||
|
||||
/* ================= ACTIONS ================= */
|
||||
async function onSave () {
|
||||
if (!canSaveUser.value) {
|
||||
$q.notify({ type: 'negative', message: 'Kaydetme yetkiniz yok' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('🟢 onSave() START', { mode: mode.value })
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="user-gateway-page flex flex-center">
|
||||
<q-page v-if="canReadUser" class="user-gateway-page flex flex-center">
|
||||
|
||||
<div class="gateway-container">
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
<!-- ➕ YENİ KULLANICI -->
|
||||
<q-card
|
||||
v-if="canWriteUser"
|
||||
class="gateway-card cursor-pointer"
|
||||
flat
|
||||
bordered
|
||||
@@ -30,6 +31,7 @@
|
||||
|
||||
<!-- 👥 MEVCUT KULLANICILAR -->
|
||||
<q-card
|
||||
v-if="canReadUser"
|
||||
class="gateway-card cursor-pointer"
|
||||
flat
|
||||
bordered
|
||||
@@ -49,21 +51,27 @@
|
||||
</div>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canWrite } = usePermission()
|
||||
const canReadUser = canRead('user')
|
||||
const canWriteUser = canWrite('user')
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
function goCreate () {
|
||||
if (!canWriteUser.value) return
|
||||
|
||||
router.push({
|
||||
path: '/app/users/new',
|
||||
query: { mode: 'new' }
|
||||
@@ -72,6 +80,7 @@ function goCreate () {
|
||||
|
||||
|
||||
function goList () {
|
||||
if (!canReadUser.value) return
|
||||
router.push({ name: 'user-list' })
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<q-page class="ol-page with-bg">
|
||||
<q-page
|
||||
v-if="canReadUser"
|
||||
class="ol-page with-bg"
|
||||
>
|
||||
|
||||
<!-- 🔍 Sticky Filter -->
|
||||
<div class="ol-filter-bar">
|
||||
@@ -27,14 +30,17 @@
|
||||
|
||||
<div class="ol-filter-actions">
|
||||
<q-btn
|
||||
v-if="canReadUser"
|
||||
label="Yenile"
|
||||
icon="refresh"
|
||||
color="primary"
|
||||
:loading="store.loading"
|
||||
:disable="!canReadUser"
|
||||
@click="store.fetchUsers"
|
||||
/>
|
||||
|
||||
<q-btn
|
||||
v-if="canWriteUser"
|
||||
label="Yeni Kullanıcı"
|
||||
icon="person_add"
|
||||
color="primary"
|
||||
@@ -68,6 +74,7 @@
|
||||
<template #body-cell-open="props">
|
||||
<q-td class="text-center">
|
||||
<q-btn
|
||||
v-if="canReadUser"
|
||||
icon="open_in_new"
|
||||
color="primary"
|
||||
flat
|
||||
@@ -149,6 +156,15 @@
|
||||
</q-banner>
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page
|
||||
v-else
|
||||
class="q-pa-md flex flex-center"
|
||||
>
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -159,9 +175,9 @@ import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const canReadUser = canRead('user')
|
||||
const canWriteUser = canWrite('user')
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
const router = useRouter()
|
||||
const store = useUserListStore()
|
||||
@@ -247,14 +263,17 @@ function splitNames(val) {
|
||||
}
|
||||
|
||||
function openDetail(id) {
|
||||
const routeName = canUpdateUser.value ? 'user-edit' : 'user-view'
|
||||
router.push({
|
||||
path: `/app/users/edit/${id}`
|
||||
name: routeName,
|
||||
params: { id: String(id) }
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
function goCreate() {
|
||||
if (!canWriteUser.value) return
|
||||
router.push({ name: 'user-new' })
|
||||
}
|
||||
|
||||
@@ -268,5 +287,9 @@ function splitPiyasalar (val) {
|
||||
}
|
||||
|
||||
|
||||
onMounted(store.fetchUsers)
|
||||
onMounted(() => {
|
||||
if (canReadUser.value) {
|
||||
store.fetchUsers()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<template>
|
||||
|
||||
<div v-if="!lookupsLoaded" class="q-pa-xl flex flex-center">
|
||||
<div v-if="canUpdateUser && !lookupsLoaded" class="q-pa-xl flex flex-center">
|
||||
<q-spinner color="primary" size="48px" />
|
||||
</div>
|
||||
|
||||
<q-page class="permissions-page">
|
||||
<q-page v-if="canUpdateUser" class="permissions-page">
|
||||
|
||||
<div class="sticky-stack">
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
color="primary"
|
||||
icon="save"
|
||||
label="Kaydet"
|
||||
@@ -115,6 +116,12 @@
|
||||
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
@@ -123,11 +130,8 @@ import { Notify } from 'quasar'
|
||||
import api from 'src/services/api'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canUpdate } = usePermission()
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
|
||||
/* ================= STATE ================= */
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md user-sync-page">
|
||||
<q-page
|
||||
v-if="canReadUser"
|
||||
class="q-pa-md user-sync-page"
|
||||
>
|
||||
|
||||
<div class="row items-center justify-between q-mb-md">
|
||||
<div class="text-h6 text-primary">👤 Kullanıcı Yönetimi</div>
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
color="primary"
|
||||
icon="sync"
|
||||
label="Sync Now"
|
||||
:loading="store.loading"
|
||||
:disable="store.loading || !canUpdateUser"
|
||||
@click="store.syncNow"
|
||||
/>
|
||||
</div>
|
||||
@@ -46,18 +51,20 @@
|
||||
<template v-slot:body-cell-actions="props">
|
||||
<q-td :props="props">
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
dense flat icon="link"
|
||||
color="primary"
|
||||
size="sm"
|
||||
@click="openMapDialog(props.row)"
|
||||
:disable="store.loading"
|
||||
:disable="store.loading || !canUpdateUser"
|
||||
/>
|
||||
<q-btn
|
||||
v-if="canUpdateUser"
|
||||
dense flat icon="link_off"
|
||||
color="negative"
|
||||
size="sm"
|
||||
@click="store.unmap(props.row.id)"
|
||||
:disable="!props.row.mssql_username"
|
||||
:disable="store.loading || !props.row.mssql_username || !canUpdateUser"
|
||||
/>
|
||||
</q-td>
|
||||
</template>
|
||||
@@ -97,6 +104,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</q-page>
|
||||
|
||||
<q-page
|
||||
v-else
|
||||
class="q-pa-md flex flex-center"
|
||||
>
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -105,11 +121,9 @@ import { useUserSyncStore } from 'src/stores/userSyncStore'
|
||||
import { Dialog } from 'quasar'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canUpdate } = usePermission()
|
||||
const canReadUser = canRead('user')
|
||||
const canUpdateUser = canUpdate('user')
|
||||
|
||||
const store = useUserSyncStore()
|
||||
|
||||
@@ -142,6 +156,10 @@ function statusColor(status) {
|
||||
}
|
||||
|
||||
function openMapDialog(pgUser) {
|
||||
if (!canUpdateUser.value) {
|
||||
return
|
||||
}
|
||||
|
||||
Dialog.create({
|
||||
title: 'Kullanıcı Eşleme',
|
||||
message: 'Bu PostgreSQL kullanıcısını hangi MSSQL kullanıcısına bağlamak istiyorsunuz?',
|
||||
@@ -158,7 +176,9 @@ function openMapDialog(pgUser) {
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
if (canReadUser.value) {
|
||||
store.loadDummy()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<q-page class="q-pa-md page-col">
|
||||
<q-page v-if="canReadFinance" class="q-pa-md page-col">
|
||||
|
||||
<!-- 🔹 Cari Kod / İsim (sabit) -->
|
||||
<div class="filter-sticky">
|
||||
@@ -135,6 +135,7 @@
|
||||
|
||||
<!-- ✅ PDF Yazdır Dropdown -->
|
||||
<q-btn-dropdown
|
||||
v-if="canExportFinance"
|
||||
flat
|
||||
color="red"
|
||||
icon="picture_as_pdf"
|
||||
@@ -258,6 +259,12 @@
|
||||
</q-table>
|
||||
</div>
|
||||
</q-page>
|
||||
|
||||
<q-page v-else class="q-pa-md flex flex-center">
|
||||
<div class="text-negative text-subtitle1">
|
||||
Bu module erisim yetkiniz yok.
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -270,11 +277,9 @@ import { useDownloadstpdfStore } from 'src/stores/downloadstpdfStore'
|
||||
import dayjs from 'dayjs'
|
||||
import { usePermission } from 'src/composables/usePermission'
|
||||
|
||||
const { canRead, canWrite, canUpdate } = usePermission()
|
||||
|
||||
const canReadOrder = canRead('order')
|
||||
const canWriteOrder = canWrite('order')
|
||||
const canUpdateOrder = canUpdate('order')
|
||||
const { canRead, canExport } = usePermission()
|
||||
const canReadFinance = canRead('finance')
|
||||
const canExportFinance = canExport('finance')
|
||||
|
||||
const $q = useQuasar()
|
||||
|
||||
@@ -458,6 +463,15 @@ function toggleLeftCols() {
|
||||
|
||||
/* 🔹 PDF İndirme Butonuna bağla */
|
||||
async function handleDownload() {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'PDF export yetkiniz yok',
|
||||
position: 'top-right'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("▶️ [DEBUG] handleDownload:", selectedCari.value, dateFrom.value, dateTo.value)
|
||||
|
||||
if (!selectedCari.value || !dateFrom.value || !dateTo.value) {
|
||||
@@ -490,6 +504,15 @@ import { useDownloadstHeadStore } from 'src/stores/downloadstHeadStore'
|
||||
const downloadstHeadStore = useDownloadstHeadStore()
|
||||
|
||||
async function CurrheadDownload() {
|
||||
if (!canExportFinance.value) {
|
||||
$q.notify({
|
||||
type: 'negative',
|
||||
message: 'PDF export yetkiniz yok',
|
||||
position: 'top-right'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
console.log("▶️ [DEBUG] CurrheadDownload:", selectedCari.value, dateFrom.value, dateTo.value)
|
||||
|
||||
if (!selectedCari.value || !dateFrom.value || !dateTo.value) {
|
||||
|
||||
@@ -2557,6 +2557,52 @@ export const useOrderEntryStore = defineStore('orderentry', {
|
||||
// =======================================================
|
||||
this.debugOrderPayload?.(header, lines, 'PRE-VALIDATE')
|
||||
|
||||
// =======================================================
|
||||
// 🧩 DUMMY CURRENCY PAYLOAD (model genişletmeden)
|
||||
// - trOrderLineCurrency için gerekli alanları satıra basar
|
||||
// - örnek satırdaki gibi: PriceVI/AmountVI = KDV dahil, Price/Amount = KDV hariç
|
||||
// =======================================================
|
||||
const r2 = (n) => Number((Number(n) || 0).toFixed(2))
|
||||
const r4 = (n) => Number((Number(n) || 0).toFixed(4))
|
||||
|
||||
for (const ln of lines) {
|
||||
const qty = Number(ln?.Qty1 || 0)
|
||||
const unitBase = Number(ln?.Price || 0) // KDV hariç birim
|
||||
const vatRate = Number(ln?.VatRate || 0)
|
||||
const exRate = Number(ln?.PriceExchangeRate || header?.ExchangeRate || 1) || 1
|
||||
|
||||
const taxBase = r2(unitBase * qty) // Amount
|
||||
const vat = r2((taxBase * vatRate) / 100) // Vat
|
||||
const net = r2(taxBase + vat) // AmountVI / NetAmount
|
||||
const unitWithVat = qty > 0 ? r4(net / qty) : r4(unitBase * (1 + vatRate / 100))
|
||||
|
||||
const docCurrency = String(ln?.DocCurrencyCode || header?.DocCurrencyCode || 'TRY').trim() || 'TRY'
|
||||
|
||||
// Backend model alanları
|
||||
ln.RelationCurrencyCode = docCurrency
|
||||
ln.DocPrice = unitWithVat
|
||||
ln.DocAmount = net
|
||||
ln.LocalPrice = unitBase
|
||||
ln.LocalAmount = taxBase
|
||||
ln.LineDiscount = Number(ln?.LineDiscount || 0)
|
||||
ln.TotalDiscount = Number(ln?.TotalDiscount || 0)
|
||||
ln.TaxBase = taxBase
|
||||
ln.Pct = Number(ln?.Pct || 0)
|
||||
ln.VatAmount = vat
|
||||
ln.VatDeducation = 0
|
||||
ln.NetAmount = net
|
||||
|
||||
// SQL kolonu isimleriyle dummy alias (decoder ignore etse de payload'da görünür)
|
||||
ln.CurrencyCode = docCurrency
|
||||
ln.ExchangeRate = exRate
|
||||
ln.PriceVI = unitWithVat
|
||||
ln.AmountVI = net
|
||||
ln.Amount = taxBase
|
||||
ln.LDiscount1 = Number(ln?.LDiscount1 || 0)
|
||||
ln.TDiscount1 = Number(ln?.TDiscount1 || 0)
|
||||
ln.Vat = vat
|
||||
}
|
||||
|
||||
// =======================================================
|
||||
// 🧪 PRE-VALIDATE — prItemVariant ön kontrol
|
||||
// - invalid varsa CREATE/UPDATE ÇALIŞMAZ
|
||||
@@ -3337,4 +3383,3 @@ export const sharedOrderEntryRefs = {
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user