Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-04-02 16:30:19 +03:00
parent 7a98652a8e
commit 028c11e042
10 changed files with 783 additions and 175 deletions

View File

@@ -576,6 +576,11 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
"order", "view", "order", "view",
wrapV3(http.HandlerFunc(routes.GetProductColorsHandler)), wrapV3(http.HandlerFunc(routes.GetProductColorsHandler)),
) )
bindV3(r, pgDB,
"/api/product-newcolors", "GET",
"order", "view",
wrapV3(http.HandlerFunc(routes.GetProductNewColorsHandler)),
)
bindV3(r, pgDB, bindV3(r, pgDB,
"/api/product-colorsize", "GET", "/api/product-colorsize", "GET",
@@ -588,6 +593,11 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
"order", "view", "order", "view",
wrapV3(http.HandlerFunc(routes.GetProductSecondColorsHandler)), wrapV3(http.HandlerFunc(routes.GetProductSecondColorsHandler)),
) )
bindV3(r, pgDB,
"/api/product-newsecondcolor", "GET",
"order", "view",
wrapV3(http.HandlerFunc(routes.GetProductNewSecondColorsHandler)),
)
bindV3(r, pgDB, bindV3(r, pgDB,
"/api/product-attributes", "GET", "/api/product-attributes", "GET",
"order", "view", "order", "view",

View File

@@ -65,10 +65,6 @@ func InsertMissingProductionVariants(mssql *sql.DB, orderHeaderID string, userna
WHERE l.OrderHeaderID = @p1 WHERE l.OrderHeaderID = @p1
AND ISNULL(l.ItemCode,'') LIKE 'U%' AND ISNULL(l.ItemCode,'') LIKE 'U%'
AND pv.ItemCode IS NULL AND pv.ItemCode IS NULL
),
MaxPlu AS (
SELECT ISNULL(MAX(PLU),0) AS BasePlu
FROM dbo.prItemVariant WITH (UPDLOCK, HOLDLOCK)
) )
INSERT INTO dbo.prItemVariant ( INSERT INTO dbo.prItemVariant (
ItemTypeCode, ItemTypeCode,
@@ -77,7 +73,6 @@ INSERT INTO dbo.prItemVariant (
ItemDim1Code, ItemDim1Code,
ItemDim2Code, ItemDim2Code,
ItemDim3Code, ItemDim3Code,
PLU,
IsSalesOrderClosed, IsSalesOrderClosed,
IsPurchaseOrderClosed, IsPurchaseOrderClosed,
IsLocked, IsLocked,
@@ -97,7 +92,6 @@ SELECT
m.ItemDim1Code, m.ItemDim1Code,
m.ItemDim2Code, m.ItemDim2Code,
m.ItemDim3Code, m.ItemDim3Code,
mp.BasePlu + ROW_NUMBER() OVER (ORDER BY m.ItemCode, m.ColorCode, m.ItemDim1Code, m.ItemDim2Code, m.ItemDim3Code),
0, 0,
0, 0,
0, 0,
@@ -109,8 +103,7 @@ SELECT
NEWID(), NEWID(),
0, 0,
0 0
FROM Missing m FROM Missing m;
CROSS JOIN MaxPlu mp;
` `
res, err := mssql.Exec(query, orderHeaderID, username) res, err := mssql.Exec(query, orderHeaderID, username)
@@ -140,17 +133,67 @@ WHERE OrderHeaderID = @p1 AND OrderLineID = @p2
return itemTypeCode, dim1, dim2, dim3, err return itemTypeCode, dim1, dim2, dim3, err
} }
type OrderLineDims struct {
ItemTypeCode int16
ItemDim1Code string
ItemDim2Code string
ItemDim3Code string
}
func GetOrderLineDimsMap(mssql *sql.DB, orderHeaderID string) (map[string]OrderLineDims, error) {
rows, err := mssql.Query(`
SELECT
CAST(OrderLineID AS NVARCHAR(50)) AS OrderLineID,
ItemTypeCode,
ISNULL(ItemDim1Code,'') AS ItemDim1Code,
ISNULL(ItemDim2Code,'') AS ItemDim2Code,
ISNULL(ItemDim3Code,'') AS ItemDim3Code
FROM dbo.trOrderLine WITH(NOLOCK)
WHERE OrderHeaderID = @p1
`, orderHeaderID)
if err != nil {
return nil, err
}
defer rows.Close()
out := make(map[string]OrderLineDims, 128)
for rows.Next() {
var lineID string
var d OrderLineDims
if err := rows.Scan(&lineID, &d.ItemTypeCode, &d.ItemDim1Code, &d.ItemDim2Code, &d.ItemDim3Code); err != nil {
return nil, err
}
out[strings.TrimSpace(lineID)] = d
}
if err := rows.Err(); err != nil {
return nil, err
}
return out, nil
}
func VariantExists(mssql *sql.DB, itemTypeCode int16, itemCode string, colorCode string, dim1 string, dim2 string, dim3 string) (bool, error) { func VariantExists(mssql *sql.DB, itemTypeCode int16, itemCode string, colorCode string, dim1 string, dim2 string, dim3 string) (bool, error) {
var exists int var exists int
err := mssql.QueryRow(` err := mssql.QueryRow(`
SELECT TOP 1 1 SELECT TOP 1 1
FROM dbo.prItemVariant FROM dbo.prItemVariant
WHERE ItemTypeCode = @p1 WHERE ItemTypeCode = @p1
AND ISNULL(LTRIM(RTRIM(ItemCode)),'') = ISNULL(LTRIM(RTRIM(@p2)),'') AND ItemCode = @p2
AND ISNULL(LTRIM(RTRIM(ColorCode)),'') = ISNULL(LTRIM(RTRIM(@p3)),'') AND (
AND ISNULL(LTRIM(RTRIM(ItemDim1Code)),'') = ISNULL(LTRIM(RTRIM(@p4)),'') ColorCode = @p3
AND ISNULL(LTRIM(RTRIM(ItemDim2Code)),'') = ISNULL(LTRIM(RTRIM(@p5)),'') OR (@p3 = '' AND (ColorCode IS NULL OR ColorCode = ''))
AND ISNULL(LTRIM(RTRIM(ItemDim3Code)),'') = ISNULL(LTRIM(RTRIM(@p6)),'') )
AND (
ItemDim1Code = @p4
OR (@p4 = '' AND (ItemDim1Code IS NULL OR ItemDim1Code = ''))
)
AND (
ItemDim2Code = @p5
OR (@p5 = '' AND (ItemDim2Code IS NULL OR ItemDim2Code = ''))
)
AND (
ItemDim3Code = @p6
OR (@p6 = '' AND (ItemDim3Code IS NULL OR ItemDim3Code = ''))
)
`, itemTypeCode, itemCode, colorCode, dim1, dim2, dim3).Scan(&exists) `, itemTypeCode, itemCode, colorCode, dim1, dim2, dim3).Scan(&exists)
if err == sql.ErrNoRows { if err == sql.ErrNoRows {
return false, nil return false, nil
@@ -171,17 +214,24 @@ func InsertMissingVariantsTx(
return 0, nil return 0, nil
} }
var basePlu int64
if err := tx.QueryRow(`
SELECT ISNULL(MAX(PLU),0) AS BasePlu
FROM dbo.prItemVariant WITH (UPDLOCK, HOLDLOCK)
`).Scan(&basePlu); err != nil {
return 0, err
}
var inserted int64 var inserted int64
ensuredItems := make(map[string]struct{}, len(missing)) ensuredItems := make(map[string]struct{}, len(missing))
for i, v := range missing { uniqueVariants := make([]models.OrderProductionMissingVariant, 0, len(missing))
seenVariants := make(map[string]struct{}, len(missing))
for _, v := range missing {
variantKey := strconv.FormatInt(int64(v.ItemTypeCode), 10) + "|" +
strings.ToUpper(strings.TrimSpace(v.ItemCode)) + "|" +
strings.ToUpper(strings.TrimSpace(v.ColorCode)) + "|" +
strings.ToUpper(strings.TrimSpace(v.ItemDim1Code)) + "|" +
strings.ToUpper(strings.TrimSpace(v.ItemDim2Code)) + "|" +
strings.ToUpper(strings.TrimSpace(v.ItemDim3Code))
if _, ok := seenVariants[variantKey]; ok {
continue
}
seenVariants[variantKey] = struct{}{}
uniqueVariants = append(uniqueVariants, v)
itemKey := strconv.FormatInt(int64(v.ItemTypeCode), 10) + "|" + v.ItemCode itemKey := strconv.FormatInt(int64(v.ItemTypeCode), 10) + "|" + v.ItemCode
if _, ok := ensuredItems[itemKey]; !ok { if _, ok := ensuredItems[itemKey]; !ok {
draft, hasDraft := cdItemByCode[itemKey] draft, hasDraft := cdItemByCode[itemKey]
@@ -198,55 +248,89 @@ FROM dbo.prItemVariant WITH (UPDLOCK, HOLDLOCK)
} }
ensuredItems[itemKey] = struct{}{} ensuredItems[itemKey] = struct{}{}
} }
}
plu := basePlu + int64(i) + 1 if len(uniqueVariants) == 0 {
res, err := tx.Exec(` return 0, nil
IF NOT EXISTS ( }
SELECT 1
FROM dbo.prItemVariant args := make([]any, 0, len(uniqueVariants)*6+1)
WHERE ItemTypeCode = @p1 valueRows := make([]string, 0, len(uniqueVariants))
AND ISNULL(LTRIM(RTRIM(ItemCode)),'') = ISNULL(LTRIM(RTRIM(@p2)),'') paramPos := 1
AND ISNULL(LTRIM(RTRIM(ColorCode)),'') = ISNULL(LTRIM(RTRIM(@p3)),'') for _, v := range uniqueVariants {
AND ISNULL(LTRIM(RTRIM(ItemDim1Code)),'') = ISNULL(LTRIM(RTRIM(@p4)),'') valueRows = append(valueRows, fmt.Sprintf("(@p%d,@p%d,@p%d,@p%d,@p%d,@p%d)", paramPos, paramPos+1, paramPos+2, paramPos+3, paramPos+4, paramPos+5))
AND ISNULL(LTRIM(RTRIM(ItemDim2Code)),'') = ISNULL(LTRIM(RTRIM(@p5)),'') args = append(args, v.ItemTypeCode, v.ItemCode, v.ColorCode, v.ItemDim1Code, v.ItemDim2Code, v.ItemDim3Code)
AND ISNULL(LTRIM(RTRIM(ItemDim3Code)),'') = ISNULL(LTRIM(RTRIM(@p6)),'') paramPos += 6
}
usernameParam := paramPos
args = append(args, username)
query := fmt.Sprintf(`
SET NOCOUNT ON;
WITH Missing(ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code) AS (
SELECT *
FROM (VALUES %s) AS v(ItemTypeCode, ItemCode, ColorCode, ItemDim1Code, ItemDim2Code, ItemDim3Code)
) )
INSERT INTO dbo.prItemVariant ( INSERT INTO dbo.prItemVariant (
ItemTypeCode, ItemTypeCode,
ItemCode, ItemCode,
ColorCode, ColorCode,
ItemDim1Code, ItemDim1Code,
ItemDim2Code, ItemDim2Code,
ItemDim3Code, ItemDim3Code,
PLU, IsSalesOrderClosed,
IsSalesOrderClosed, IsPurchaseOrderClosed,
IsPurchaseOrderClosed, IsLocked,
IsLocked, IsBlocked,
IsBlocked, CreatedUserName,
CreatedUserName, CreatedDate,
CreatedDate, LastUpdatedUserName,
LastUpdatedUserName, LastUpdatedDate,
LastUpdatedDate, RowGuid,
RowGuid, UseInternet,
UseInternet, IsStoreOrderClosed
IsStoreOrderClosed
) )
VALUES ( SELECT
@p1, @p2, @p3, @p4, @p5, @p6, m.ItemTypeCode,
@p7, m.ItemCode,
0, 0, 0, 0, m.ColorCode,
@p8, GETDATE(), @p8, GETDATE(), m.ItemDim1Code,
NEWID(), m.ItemDim2Code,
0, m.ItemDim3Code,
0 0, 0, 0, 0,
); @p%d, GETDATE(), @p%d, GETDATE(),
`, v.ItemTypeCode, v.ItemCode, v.ColorCode, v.ItemDim1Code, v.ItemDim2Code, v.ItemDim3Code, plu, username) NEWID(),
if err != nil { 0,
return inserted, err 0
} FROM Missing m
if rows, err := res.RowsAffected(); err == nil { LEFT JOIN dbo.prItemVariant pv
inserted += rows ON pv.ItemTypeCode = m.ItemTypeCode
} AND pv.ItemCode = m.ItemCode
AND (
pv.ColorCode = m.ColorCode
OR (m.ColorCode = '' AND (pv.ColorCode IS NULL OR pv.ColorCode = ''))
)
AND (
pv.ItemDim1Code = m.ItemDim1Code
OR (m.ItemDim1Code = '' AND (pv.ItemDim1Code IS NULL OR pv.ItemDim1Code = ''))
)
AND (
pv.ItemDim2Code = m.ItemDim2Code
OR (m.ItemDim2Code = '' AND (pv.ItemDim2Code IS NULL OR pv.ItemDim2Code = ''))
)
AND (
pv.ItemDim3Code = m.ItemDim3Code
OR (m.ItemDim3Code = '' AND (pv.ItemDim3Code IS NULL OR pv.ItemDim3Code = ''))
)
WHERE pv.ItemCode IS NULL;
`, strings.Join(valueRows, ","), usernameParam, usernameParam)
res, err := tx.Exec(query, args...)
if err != nil {
return inserted, err
}
if rows, rowsErr := res.RowsAffected(); rowsErr == nil {
inserted += rows
} }
return inserted, nil return inserted, nil
} }
@@ -341,7 +425,7 @@ BEGIN
'AD', '', 0, 0, 'AD', '', 0, 0,
1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
'', '10%', '', '', '', '', '%10', '', '', '',
'', '', '0', '0', '0', '', '', '0', '0', '0',
'0', '', '', 0, '', '1', '0', '', '', 0, '', '1',
0, 0, '1900-01-01', 0, 0, 0, 0, '1900-01-01', 0, 0,
@@ -369,7 +453,15 @@ SET
ProductHierarchyID = COALESCE(@p5, ProductHierarchyID), ProductHierarchyID = COALESCE(@p5, ProductHierarchyID),
UnitOfMeasureCode1 = COALESCE(NULLIF(@p6,''), UnitOfMeasureCode1), UnitOfMeasureCode1 = COALESCE(NULLIF(@p6,''), UnitOfMeasureCode1),
ItemAccountGrCode = COALESCE(NULLIF(@p7,''), ItemAccountGrCode), ItemAccountGrCode = COALESCE(NULLIF(@p7,''), ItemAccountGrCode),
ItemTaxGrCode = COALESCE(NULLIF(@p8,''), ItemTaxGrCode), ItemTaxGrCode = CASE
WHEN NULLIF(@p8,'') IS NULL THEN ItemTaxGrCode
WHEN EXISTS (
SELECT 1
FROM dbo.cdItemTaxGr g WITH(NOLOCK)
WHERE LTRIM(RTRIM(g.ItemTaxGrCode)) = LTRIM(RTRIM(@p8))
) THEN @p8
ELSE ItemTaxGrCode
END,
ItemPaymentPlanGrCode = COALESCE(NULLIF(@p9,''), ItemPaymentPlanGrCode), ItemPaymentPlanGrCode = COALESCE(NULLIF(@p9,''), ItemPaymentPlanGrCode),
ItemDiscountGrCode = COALESCE(NULLIF(@p10,''), ItemDiscountGrCode), ItemDiscountGrCode = COALESCE(NULLIF(@p10,''), ItemDiscountGrCode),
ItemVendorGrCode = COALESCE(NULLIF(@p11,''), ItemVendorGrCode), ItemVendorGrCode = COALESCE(NULLIF(@p11,''), ItemVendorGrCode),
@@ -411,23 +503,67 @@ WHERE ItemTypeCode = @p1
} }
func UpdateOrderLinesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderProductionUpdateLine, username string) (int64, error) { func UpdateOrderLinesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderProductionUpdateLine, username string) (int64, error) {
if len(lines) == 0 {
return 0, nil
}
const chunkSize = 300
var updated int64 var updated int64
for _, line := range lines {
res, err := tx.Exec(` for i := 0; i < len(lines); i += chunkSize {
UPDATE dbo.trOrderLine end := i + chunkSize
SET if end > len(lines) {
ItemCode = @p1, end = len(lines)
ColorCode = @p2,
ItemDim2Code = @p3,
LineDescription = COALESCE(NULLIF(@p4,''), LineDescription),
LastUpdatedUserName = @p5,
LastUpdatedDate = GETDATE()
WHERE OrderHeaderID = @p6 AND OrderLineID = @p7
`, line.NewItemCode, line.NewColor, line.NewDim2, line.NewDesc, username, orderHeaderID, line.OrderLineID)
if err != nil {
return updated, err
} }
if rows, err := res.RowsAffected(); err == nil { chunk := lines[i:end]
values := make([]string, 0, len(chunk))
args := make([]any, 0, len(chunk)*5+2)
paramPos := 1
for _, line := range chunk {
values = append(values, fmt.Sprintf("(@p%d,@p%d,@p%d,@p%d,@p%d)", paramPos, paramPos+1, paramPos+2, paramPos+3, paramPos+4))
args = append(args,
strings.TrimSpace(line.OrderLineID),
line.NewItemCode,
line.NewColor,
line.NewDim2,
line.NewDesc,
)
paramPos += 5
}
orderHeaderParam := paramPos
usernameParam := paramPos + 1
args = append(args, orderHeaderID, username)
query := fmt.Sprintf(`
SET NOCOUNT ON;
WITH src (OrderLineID, NewItemCode, NewColor, NewDim2, NewDesc) AS (
SELECT *
FROM (VALUES %s) AS v (OrderLineID, NewItemCode, NewColor, NewDim2, NewDesc)
)
UPDATE l
SET
l.ItemCode = s.NewItemCode,
l.ColorCode = s.NewColor,
l.ItemDim2Code = s.NewDim2,
l.LineDescription = COALESCE(NULLIF(s.NewDesc,''), l.LineDescription),
l.LastUpdatedUserName = @p%d,
l.LastUpdatedDate = GETDATE()
FROM dbo.trOrderLine l
JOIN src s
ON CAST(l.OrderLineID AS NVARCHAR(50)) = s.OrderLineID
WHERE l.OrderHeaderID = @p%d;
`, strings.Join(values, ","), usernameParam, orderHeaderParam)
chunkStart := time.Now()
res, execErr := tx.Exec(query, args...)
if execErr != nil {
return updated, fmt.Errorf("update lines chunk failed chunkStart=%d chunkEnd=%d duration_ms=%d: %w", i, end, time.Since(chunkStart).Milliseconds(), execErr)
}
log.Printf("[UpdateOrderLinesTx] orderHeaderID=%s chunk=%d-%d duration_ms=%d", orderHeaderID, i, end, time.Since(chunkStart).Milliseconds())
if rows, rowsErr := res.RowsAffected(); rowsErr == nil {
updated += rows updated += rows
} }
} }
@@ -439,58 +575,89 @@ func UpsertItemAttributesTx(tx *sql.Tx, attrs []models.OrderProductionItemAttrib
return 0, nil return 0, nil
} }
// SQL Server parameter limiti (2100) nedeniyle batch'li set-based upsert kullanilir.
const chunkSize = 400 // 400 * 4 param + 1 username = 1601
var affected int64 var affected int64
for _, a := range attrs { for i := 0; i < len(attrs); i += chunkSize {
res, err := tx.Exec(` end := i + chunkSize
IF EXISTS ( if end > len(attrs) {
SELECT 1 end = len(attrs)
FROM dbo.prItemAttribute }
WHERE ItemTypeCode = @p1 chunk := attrs[i:end]
AND ItemCode = @p2
AND AttributeTypeCode = @p3 values := make([]string, 0, len(chunk))
args := make([]any, 0, len(chunk)*4+1)
paramPos := 1
for _, a := range chunk {
values = append(values, fmt.Sprintf("(@p%d,@p%d,@p%d,@p%d)", paramPos, paramPos+1, paramPos+2, paramPos+3))
args = append(args, a.ItemTypeCode, a.ItemCode, a.AttributeTypeCode, a.AttributeCode)
paramPos += 4
}
usernameParam := paramPos
args = append(args, username)
query := fmt.Sprintf(`
SET NOCOUNT ON;
DECLARE @updated INT = 0;
DECLARE @inserted INT = 0;
WITH src (ItemTypeCode, ItemCode, AttributeTypeCode, AttributeCode) AS (
SELECT *
FROM (VALUES %s) AS v (ItemTypeCode, ItemCode, AttributeTypeCode, AttributeCode)
) )
BEGIN UPDATE tgt
UPDATE dbo.prItemAttribute SET
SET tgt.AttributeCode = src.AttributeCode,
AttributeCode = @p4, tgt.LastUpdatedUserName = @p%d,
LastUpdatedUserName = @p5, tgt.LastUpdatedDate = GETDATE()
LastUpdatedDate = GETDATE() FROM dbo.prItemAttribute tgt
WHERE ItemTypeCode = @p1 JOIN src
AND ItemCode = @p2 ON src.ItemTypeCode = tgt.ItemTypeCode
AND AttributeTypeCode = @p3 AND src.ItemCode = tgt.ItemCode
END AND src.AttributeTypeCode = tgt.AttributeTypeCode;
ELSE SET @updated = @@ROWCOUNT;
BEGIN
INSERT INTO dbo.prItemAttribute ( WITH src (ItemTypeCode, ItemCode, AttributeTypeCode, AttributeCode) AS (
ItemTypeCode, SELECT *
ItemCode, FROM (VALUES %s) AS v (ItemTypeCode, ItemCode, AttributeTypeCode, AttributeCode)
AttributeTypeCode, )
AttributeCode, INSERT INTO dbo.prItemAttribute (
CreatedUserName, ItemTypeCode,
CreatedDate, ItemCode,
LastUpdatedUserName, AttributeTypeCode,
LastUpdatedDate, AttributeCode,
RowGuid CreatedUserName,
) CreatedDate,
VALUES ( LastUpdatedUserName,
@p1, LastUpdatedDate,
@p2, RowGuid
@p3, )
@p4, SELECT
@p5, src.ItemTypeCode,
GETDATE(), src.ItemCode,
@p5, src.AttributeTypeCode,
GETDATE(), src.AttributeCode,
NEWID() @p%d,
) GETDATE(),
END @p%d,
`, a.ItemTypeCode, a.ItemCode, a.AttributeTypeCode, a.AttributeCode, username) GETDATE(),
if err != nil { NEWID()
FROM src
LEFT JOIN dbo.prItemAttribute tgt
ON src.ItemTypeCode = tgt.ItemTypeCode
AND src.ItemCode = tgt.ItemCode
AND src.AttributeTypeCode = tgt.AttributeTypeCode
WHERE tgt.ItemCode IS NULL;
SET @inserted = @@ROWCOUNT;
SELECT (@updated + @inserted) AS Affected;
`, strings.Join(values, ","), usernameParam, strings.Join(values, ","), usernameParam, usernameParam)
var chunkAffected int64
if err := tx.QueryRow(query, args...).Scan(&chunkAffected); err != nil {
return affected, err return affected, err
} }
if rows, err := res.RowsAffected(); err == nil { affected += chunkAffected
affected += rows
}
} }
return affected, nil return affected, nil
} }

View File

@@ -0,0 +1,16 @@
package queries
const GetProductNewColors = `
SELECT
CAST(@p1 AS NVARCHAR(30)) AS ProductCode,
LTRIM(RTRIM(c.ColorCode)) AS ColorCode,
ISNULL(NULLIF(LTRIM(RTRIM(cd.ColorDescription)), ''), ISNULL(NULLIF(LTRIM(RTRIM(c.ColorHex)), ''), LTRIM(RTRIM(c.ColorCode)))) AS ColorDescription
FROM dbo.cdColor AS c WITH(NOLOCK)
LEFT JOIN dbo.cdColorDesc AS cd WITH(NOLOCK)
ON cd.ColorCode = c.ColorCode
AND cd.LangCode = 'TR'
WHERE ISNULL(c.IsBlocked, 0) = 0
AND LEN(LTRIM(RTRIM(ISNULL(c.ColorCode, '')))) = 3
AND LTRIM(RTRIM(ISNULL(c.ColorCatalogCode1, ''))) = N'ÜRÜN'
ORDER BY LTRIM(RTRIM(c.ColorCode));
`

View File

@@ -0,0 +1,16 @@
package queries
const GetProductNewSecondColors = `
SELECT
LTRIM(RTRIM(@ProductCode)) AS ProductCode,
LTRIM(RTRIM(ISNULL(@ColorCode, ''))) AS ColorCode,
LTRIM(RTRIM(d2.ItemDim2Code)) AS ItemDim2Code,
ISNULL(NULLIF(LTRIM(RTRIM(cd.ColorDescription)), ''), LTRIM(RTRIM(d2.ItemDim2Code))) AS ColorDescription
FROM dbo.cdItemDim2 AS d2 WITH(NOLOCK)
LEFT JOIN dbo.cdColorDesc AS cd WITH(NOLOCK)
ON cd.ColorCode = d2.ItemDim2Code
AND cd.LangCode = 'TR'
WHERE ISNULL(d2.IsBlocked, 0) = 0
AND LEN(LTRIM(RTRIM(ISNULL(d2.ItemDim2Code, '')))) = 3
ORDER BY LTRIM(RTRIM(d2.ItemDim2Code));
`

View File

@@ -150,6 +150,9 @@ func OrderProductionInsertMissingRoute(mssql *sql.DB) http.Handler {
func OrderProductionValidateRoute(mssql *sql.DB) http.Handler { func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
rid := fmt.Sprintf("opv-%d", time.Now().UnixNano())
w.Header().Set("X-Debug-Request-Id", rid)
start := time.Now()
id := mux.Vars(r)["id"] id := mux.Vars(r)["id"]
if id == "" { if id == "" {
@@ -167,11 +170,16 @@ func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
return return
} }
stepStart := time.Now()
missing, err := buildMissingVariants(mssql, id, payload.Lines) missing, err := buildMissingVariants(mssql, id, payload.Lines)
if err != nil { if err != nil {
log.Printf("[OrderProductionValidateRoute] rid=%s orderHeaderID=%s step=build_missing failed duration_ms=%d err=%v",
rid, id, time.Since(stepStart).Milliseconds(), err)
writeDBError(w, http.StatusInternalServerError, "validate_missing_variants", id, "", len(payload.Lines), err) writeDBError(w, http.StatusInternalServerError, "validate_missing_variants", id, "", len(payload.Lines), err)
return return
} }
log.Printf("[OrderProductionValidateRoute] rid=%s orderHeaderID=%s lineCount=%d missingCount=%d build_missing_ms=%d total_ms=%d",
rid, id, len(payload.Lines), len(missing), time.Since(stepStart).Milliseconds(), time.Since(start).Milliseconds())
resp := map[string]any{ resp := map[string]any{
"missingCount": len(missing), "missingCount": len(missing),
@@ -189,6 +197,9 @@ func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
func OrderProductionApplyRoute(mssql *sql.DB) http.Handler { func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8") w.Header().Set("Content-Type", "application/json; charset=utf-8")
rid := fmt.Sprintf("opa-%d", time.Now().UnixNano())
w.Header().Set("X-Debug-Request-Id", rid)
start := time.Now()
id := mux.Vars(r)["id"] id := mux.Vars(r)["id"]
if id == "" { if id == "" {
@@ -206,13 +217,20 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
return return
} }
stepMissingStart := time.Now()
missing, err := buildMissingVariants(mssql, id, payload.Lines) missing, err := buildMissingVariants(mssql, id, payload.Lines)
if err != nil { if err != nil {
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=build_missing failed duration_ms=%d err=%v",
rid, id, time.Since(stepMissingStart).Milliseconds(), err)
writeDBError(w, http.StatusInternalServerError, "apply_validate_missing_variants", id, "", len(payload.Lines), err) writeDBError(w, http.StatusInternalServerError, "apply_validate_missing_variants", id, "", len(payload.Lines), err)
return return
} }
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s lineCount=%d missingCount=%d build_missing_ms=%d",
rid, id, len(payload.Lines), len(missing), time.Since(stepMissingStart).Milliseconds())
if len(missing) > 0 && !payload.InsertMissing { if len(missing) > 0 && !payload.InsertMissing {
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s early_exit=missing_variants total_ms=%d",
rid, id, time.Since(start).Milliseconds())
w.WriteHeader(http.StatusConflict) w.WriteHeader(http.StatusConflict)
_ = json.NewEncoder(w).Encode(map[string]any{ _ = json.NewEncoder(w).Encode(map[string]any{
"missingCount": len(missing), "missingCount": len(missing),
@@ -231,43 +249,68 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
username = "system" username = "system"
} }
stepBeginStart := time.Now()
tx, err := mssql.Begin() tx, err := mssql.Begin()
if err != nil { if err != nil {
writeDBError(w, http.StatusInternalServerError, "begin_tx", id, username, len(payload.Lines), err) writeDBError(w, http.StatusInternalServerError, "begin_tx", id, username, len(payload.Lines), err)
return return
} }
defer tx.Rollback() defer tx.Rollback()
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=begin_tx duration_ms=%d", rid, id, time.Since(stepBeginStart).Milliseconds())
stepTxSettingsStart := time.Now()
if _, err := tx.Exec(`SET XACT_ABORT ON; SET LOCK_TIMEOUT 15000;`); err != nil {
writeDBError(w, http.StatusInternalServerError, "tx_settings", id, username, len(payload.Lines), err)
return
}
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=tx_settings duration_ms=%d", rid, id, time.Since(stepTxSettingsStart).Milliseconds())
var inserted int64 var inserted int64
if payload.InsertMissing { if payload.InsertMissing {
cdItemByCode := buildCdItemDraftMap(payload.CdItems) cdItemByCode := buildCdItemDraftMap(payload.CdItems)
stepInsertMissingStart := time.Now()
inserted, err = queries.InsertMissingVariantsTx(tx, missing, username, cdItemByCode) inserted, err = queries.InsertMissingVariantsTx(tx, missing, username, cdItemByCode)
if err != nil { if err != nil {
writeDBError(w, http.StatusInternalServerError, "insert_missing_variants", id, username, len(missing), err) writeDBError(w, http.StatusInternalServerError, "insert_missing_variants", id, username, len(missing), err)
return return
} }
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=insert_missing inserted=%d duration_ms=%d",
rid, id, inserted, time.Since(stepInsertMissingStart).Milliseconds())
} }
stepValidateAttrStart := time.Now()
if err := validateProductAttributes(payload.ProductAttributes); err != nil { if err := validateProductAttributes(payload.ProductAttributes); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
attributeAffected, err := queries.UpsertItemAttributesTx(tx, payload.ProductAttributes, username) log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=validate_attributes count=%d duration_ms=%d",
if err != nil { rid, id, len(payload.ProductAttributes), time.Since(stepValidateAttrStart).Milliseconds())
writeDBError(w, http.StatusInternalServerError, "upsert_item_attributes", id, username, len(payload.ProductAttributes), err)
return
}
stepUpdateLinesStart := time.Now()
updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username) updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username)
if err != nil { if err != nil {
writeDBError(w, http.StatusInternalServerError, "update_order_lines", id, username, len(payload.Lines), err) writeDBError(w, http.StatusInternalServerError, "update_order_lines", id, username, len(payload.Lines), err)
return return
} }
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=update_lines updated=%d duration_ms=%d",
rid, id, updated, time.Since(stepUpdateLinesStart).Milliseconds())
stepUpsertAttrStart := time.Now()
attributeAffected, err := queries.UpsertItemAttributesTx(tx, payload.ProductAttributes, username)
if err != nil {
writeDBError(w, http.StatusInternalServerError, "upsert_item_attributes", id, username, len(payload.ProductAttributes), err)
return
}
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=upsert_attributes affected=%d duration_ms=%d",
rid, id, attributeAffected, time.Since(stepUpsertAttrStart).Milliseconds())
stepCommitStart := time.Now()
if err := tx.Commit(); err != nil { if err := tx.Commit(); err != nil {
writeDBError(w, http.StatusInternalServerError, "commit_tx", id, username, len(payload.Lines), err) writeDBError(w, http.StatusInternalServerError, "commit_tx", id, username, len(payload.Lines), err)
return return
} }
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=commit duration_ms=%d total_ms=%d",
rid, id, time.Since(stepCommitStart).Milliseconds(), time.Since(start).Milliseconds())
resp := map[string]any{ resp := map[string]any{
"updated": updated, "updated": updated,
@@ -319,7 +362,13 @@ func buildCdItemDraftMap(list []models.OrderProductionCdItemDraft) map[string]mo
} }
func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) { func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) {
start := time.Now()
missing := make([]models.OrderProductionMissingVariant, 0) missing := make([]models.OrderProductionMissingVariant, 0)
lineDimsMap, err := queries.GetOrderLineDimsMap(mssql, orderHeaderID)
if err != nil {
return nil, err
}
existsCache := make(map[string]bool, len(lines))
for _, line := range lines { for _, line := range lines {
lineID := strings.TrimSpace(line.OrderLineID) lineID := strings.TrimSpace(line.OrderLineID)
@@ -331,28 +380,43 @@ func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.Or
continue continue
} }
itemTypeCode, dim1, _, dim3, err := queries.GetOrderLineDims(mssql, orderHeaderID, lineID) dims, ok := lineDimsMap[lineID]
if err != nil { if !ok {
return nil, err continue
} }
exists, err := queries.VariantExists(mssql, itemTypeCode, newItem, newColor, dim1, newDim2, dim3) cacheKey := fmt.Sprintf("%d|%s|%s|%s|%s|%s",
if err != nil { dims.ItemTypeCode,
return nil, err strings.ToUpper(strings.TrimSpace(newItem)),
strings.ToUpper(strings.TrimSpace(newColor)),
strings.ToUpper(strings.TrimSpace(dims.ItemDim1Code)),
strings.ToUpper(strings.TrimSpace(newDim2)),
strings.ToUpper(strings.TrimSpace(dims.ItemDim3Code)),
)
exists, cached := existsCache[cacheKey]
if !cached {
var checkErr error
exists, checkErr = queries.VariantExists(mssql, dims.ItemTypeCode, newItem, newColor, dims.ItemDim1Code, newDim2, dims.ItemDim3Code)
if checkErr != nil {
return nil, checkErr
}
existsCache[cacheKey] = exists
} }
if !exists { if !exists {
missing = append(missing, models.OrderProductionMissingVariant{ missing = append(missing, models.OrderProductionMissingVariant{
OrderLineID: lineID, OrderLineID: lineID,
ItemTypeCode: itemTypeCode, ItemTypeCode: dims.ItemTypeCode,
ItemCode: newItem, ItemCode: newItem,
ColorCode: newColor, ColorCode: newColor,
ItemDim1Code: dim1, ItemDim1Code: dims.ItemDim1Code,
ItemDim2Code: newDim2, ItemDim2Code: newDim2,
ItemDim3Code: dim3, ItemDim3Code: dims.ItemDim3Code,
}) })
} }
} }
log.Printf("[buildMissingVariants] orderHeaderID=%s lineCount=%d dimMapCount=%d missingCount=%d total_ms=%d",
orderHeaderID, len(lines), len(lineDimsMap), len(missing), time.Since(start).Milliseconds())
return missing, nil return missing, nil
} }

View File

@@ -0,0 +1,45 @@
package routes
import (
"bssapp-backend/auth"
"bssapp-backend/db"
"bssapp-backend/models"
"bssapp-backend/queries"
"encoding/json"
"log"
"net/http"
)
func GetProductNewColorsHandler(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
code := r.URL.Query().Get("code")
if code == "" {
http.Error(w, "Eksik parametre: code gerekli", http.StatusBadRequest)
return
}
rows, err := db.MssqlDB.Query(queries.GetProductNewColors, code)
if err != nil {
http.Error(w, "Yeni urun renk listesi alinamadi: "+err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
var list []models.ProductColor
for rows.Next() {
var c models.ProductColor
if err := rows.Scan(&c.ProductCode, &c.ColorCode, &c.ColorDescription); err != nil {
log.Println("Satir okunamadi:", err)
continue
}
list = append(list, c)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(list)
}

View File

@@ -0,0 +1,51 @@
package routes
import (
"bssapp-backend/auth"
"bssapp-backend/db"
"bssapp-backend/models"
"bssapp-backend/queries"
"database/sql"
"encoding/json"
"log"
"net/http"
)
func GetProductNewSecondColorsHandler(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
code := r.URL.Query().Get("code")
color := r.URL.Query().Get("color")
if code == "" || color == "" {
http.Error(w, "Eksik parametre: code ve color gerekli", http.StatusBadRequest)
return
}
rows, err := db.MssqlDB.Query(
queries.GetProductNewSecondColors,
sql.Named("ProductCode", code),
sql.Named("ColorCode", color),
)
if err != nil {
http.Error(w, "Yeni urun 2. renk listesi alinamadi: "+err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
var list []models.ProductSecondColor
for rows.Next() {
var c models.ProductSecondColor
if err := rows.Scan(&c.ProductCode, &c.ColorCode, &c.ItemDim2Code, &c.ColorDescription); err != nil {
log.Println("Satir okunamadi:", err)
continue
}
list = append(list, c)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(list)
}

View File

@@ -216,6 +216,7 @@
filled filled
label="Yeni 2. Renk" label="Yeni 2. Renk"
:disable="isColorSelectionLocked(props.row)" :disable="isColorSelectionLocked(props.row)"
@update:model-value="() => onNewDim2Change(props.row)"
/> />
</q-td> </q-td>
</template> </template>
@@ -331,6 +332,13 @@ const store = useOrderProductionItemStore()
const BAGGI_CODE_PATTERN = /^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$/ const BAGGI_CODE_PATTERN = /^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$/
const BAGGI_CODE_ERROR = 'Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999' const BAGGI_CODE_ERROR = 'Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999'
function nowMs () {
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
return performance.now()
}
return Date.now()
}
const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim()) const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim())
const header = computed(() => store.header || {}) const header = computed(() => store.header || {})
const cariLabel = computed(() => { const cariLabel = computed(() => {
@@ -517,8 +525,13 @@ function onNewItemChange (row, val, source = 'typed') {
applyNewItemVisualState(row, source) applyNewItemVisualState(row, source)
row.NewColor = '' row.NewColor = ''
row.NewDim2 = '' row.NewDim2 = ''
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
if (row.NewItemCode) { if (row.NewItemCode) {
store.fetchColors(row.NewItemCode) if (row.NewItemMode === 'new') {
store.fetchNewColors(row.NewItemCode)
} else {
store.fetchColors(row.NewItemCode)
}
} }
if (row.NewItemMode === 'new' && isValidBaggiModelCode(row.NewItemCode) && row.NewItemCode !== prevCode) { if (row.NewItemMode === 'new' && isValidBaggiModelCode(row.NewItemCode) && row.NewItemCode !== prevCode) {
openNewCodeSetupFlow(row.NewItemCode) openNewCodeSetupFlow(row.NewItemCode)
@@ -560,14 +573,27 @@ function openNewCodeSetupFlow (itemCode) {
function onNewColorChange (row) { function onNewColorChange (row) {
row.NewColor = normalizeShortCode(row.NewColor, 3) row.NewColor = normalizeShortCode(row.NewColor, 3)
row.NewDim2 = '' row.NewDim2 = ''
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
if (row.NewItemCode && row.NewColor) { if (row.NewItemCode && row.NewColor) {
store.fetchSecondColors(row.NewItemCode, row.NewColor) if (String(row?.NewItemMode || '').trim() === 'new') {
store.fetchNewSecondColors(row.NewItemCode, row.NewColor)
} else {
store.fetchSecondColors(row.NewItemCode, row.NewColor)
}
} }
} }
function onNewDim2Change (row) {
row.NewDim2 = normalizeShortCode(row.NewDim2, 3)
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
}
function getColorOptions (row) { function getColorOptions (row) {
const code = row?.NewItemCode || '' const code = row?.NewItemCode || ''
const list = store.colorOptionsByCode[code] || [] const isNewMode = String(row?.NewItemMode || '').trim() === 'new'
const list = isNewMode
? (store.newColorOptionsByCode[code] || [])
: (store.colorOptionsByCode[code] || [])
return list.map(c => ({ return list.map(c => ({
...c, ...c,
colorLabel: `${c.color_code} - ${c.color_description || ''}`.trim() colorLabel: `${c.color_code} - ${c.color_description || ''}`.trim()
@@ -578,7 +604,10 @@ function getSecondColorOptions (row) {
const code = row?.NewItemCode || '' const code = row?.NewItemCode || ''
const color = row?.NewColor || '' const color = row?.NewColor || ''
const key = `${code}::${color}` const key = `${code}::${color}`
const list = store.secondColorOptionsByKey[key] || [] const isNewMode = String(row?.NewItemMode || '').trim() === 'new'
const list = isNewMode
? (store.newSecondColorOptionsByKey[key] || [])
: (store.secondColorOptionsByKey[key] || [])
return list.map(c => ({ return list.map(c => ({
...c, ...c,
item_dim2_label: `${c.item_dim2_code} - ${c.color_description || ''}`.trim() item_dim2_label: `${c.item_dim2_code} - ${c.color_description || ''}`.trim()
@@ -609,6 +638,55 @@ function isValidBaggiModelCode (code) {
return BAGGI_CODE_PATTERN.test(code) return BAGGI_CODE_PATTERN.test(code)
} }
function formatCodeColorDim2 (itemCode, color, dim2) {
const item = String(itemCode || '').trim().toUpperCase()
const c1 = normalizeShortCode(color, 3)
const c2 = normalizeShortCode(dim2, 3)
const c1Safe = c1 || '-'
const c2Safe = c2 || '-'
return `${item}/${c1Safe}/${c2Safe}`
}
function buildAutoUpdateNote (row) {
const oldInfo = formatCodeColorDim2(row?.OldItemCode, row?.OldColor, row?.OldDim2)
const nextInfo = formatCodeColorDim2(row?.NewItemCode, row?.NewColor, row?.NewDim2)
return `Bu siparis satirinda kod ${oldInfo} bilgisinden ${nextInfo} bilgisine guncellenmistir.`
}
function isSelectionCompleteByOldShape (row) {
const hasModel = String(row?.NewItemCode || '').trim().length > 0
if (!hasModel) return false
const oldHasColor = String(row?.OldColor || '').trim().length > 0
const oldHasDim2 = String(row?.OldDim2 || '').trim().length > 0
const hasNewColor = normalizeShortCode(row?.NewColor, 3).length === 3
const hasNewDim2 = normalizeShortCode(row?.NewDim2, 3).length === 3
if (oldHasDim2) return hasNewColor && hasNewDim2
if (oldHasColor) return hasNewColor
return true
}
function stripAutoUpdateNote (text) {
const desc = String(text || '').trim()
if (!desc) return ''
const marker = ' Bu siparis satirinda kod '
const idx = desc.indexOf(marker)
if (idx > -1) return desc.slice(0, idx).trim()
if (desc.startsWith('Bu siparis satirinda kod ')) return ''
return desc
}
function mergeDescWithAutoNote (row, baseDesc) {
const desc = stripAutoUpdateNote(baseDesc)
if (!isSelectionCompleteByOldShape(row)) return desc
const note = buildAutoUpdateNote(row)
if (!note) return desc
if (desc.includes(note)) return desc
if (!desc) return note
return `${desc} ${note}`
}
function validateRowInput (row) { function validateRowInput (row) {
const entryMode = String(row?.NewItemEntryMode || '').trim() const entryMode = String(row?.NewItemEntryMode || '').trim()
const newItemCode = String(row.NewItemCode || '').trim().toUpperCase() const newItemCode = String(row.NewItemCode || '').trim().toUpperCase()
@@ -631,6 +709,7 @@ function validateRowInput (row) {
row.NewItemCode = newItemCode row.NewItemCode = newItemCode
row.NewColor = newColor row.NewColor = newColor
row.NewDim2 = newDim2 row.NewDim2 = newDim2
row.NewDesc = mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
return '' return ''
} }
@@ -646,7 +725,7 @@ function collectLinesFromRows (selectedRows) {
NewItemCode: String(row.NewItemCode || '').trim().toUpperCase(), NewItemCode: String(row.NewItemCode || '').trim().toUpperCase(),
NewColor: normalizeShortCode(row.NewColor, 3), NewColor: normalizeShortCode(row.NewColor, 3),
NewDim2: normalizeShortCode(row.NewDim2, 3), NewDim2: normalizeShortCode(row.NewDim2, 3),
NewDesc: String((row.NewDesc || row.OldDesc) || '').trim() NewDesc: mergeDescWithAutoNote(row, row.NewDesc || row.OldDesc)
} }
for (const id of (row.OrderLineIDs || [])) { for (const id of (row.OrderLineIDs || [])) {
@@ -668,7 +747,7 @@ function createEmptyCdItemDraft (itemCode) {
ProductHierarchyID: '', ProductHierarchyID: '',
UnitOfMeasureCode1: 'AD', UnitOfMeasureCode1: 'AD',
ItemAccountGrCode: '', ItemAccountGrCode: '',
ItemTaxGrCode: '10%', ItemTaxGrCode: '%10',
ItemPaymentPlanGrCode: '', ItemPaymentPlanGrCode: '',
ItemDiscountGrCode: '', ItemDiscountGrCode: '',
ItemVendorGrCode: '', ItemVendorGrCode: '',
@@ -715,11 +794,6 @@ function isDummyLookupOption (key, codeRaw, descRaw) {
if (code === '0' || code === '00' || code === '000' || code === '0000') return true if (code === '0' || code === '00' || code === '000' || code === '0000') return true
if (desc.includes('DUMMY')) return true if (desc.includes('DUMMY')) return true
// Is plani dokumanindaki sari/default alanlar
if (key === 'unitOfMeasureCode1List' && code === 'AD') return true
if (key === 'itemTaxGrCodes' && code === '10%') return true
if (key === 'companyCodes' && code === '1') return true
return false return false
} }
@@ -755,19 +829,19 @@ function normalizeCdItemDraftForPayload (draftRaw) {
ItemTypeCode: toIntOrNil(d.ItemTypeCode) || 1, ItemTypeCode: toIntOrNil(d.ItemTypeCode) || 1,
ItemCode: String(d.ItemCode || '').trim().toUpperCase(), ItemCode: String(d.ItemCode || '').trim().toUpperCase(),
ItemDimTypeCode: toIntOrNil(d.ItemDimTypeCode) || 1, ItemDimTypeCode: toIntOrNil(d.ItemDimTypeCode) || 1,
ProductTypeCode: 1, ProductTypeCode: null,
ProductHierarchyID: toIntOrNil(d.ProductHierarchyID), ProductHierarchyID: toIntOrNil(d.ProductHierarchyID),
UnitOfMeasureCode1: 'AD', UnitOfMeasureCode1: 'AD',
ItemAccountGrCode: null, ItemAccountGrCode: null,
ItemTaxGrCode: '10%', ItemTaxGrCode: '%10',
ItemPaymentPlanGrCode: null, ItemPaymentPlanGrCode: null,
ItemDiscountGrCode: null, ItemDiscountGrCode: null,
ItemVendorGrCode: null, ItemVendorGrCode: null,
PromotionGroupCode: null, PromotionGroupCode: null,
ProductCollectionGrCode: '0', ProductCollectionGrCode: null,
StorePriceLevelCode: '0', StorePriceLevelCode: null,
PerceptionOfFashionCode: '0', PerceptionOfFashionCode: null,
CommercialRoleCode: '0', CommercialRoleCode: null,
StoreCapacityLevelCode: null, StoreCapacityLevelCode: null,
CustomsTariffNumberCode: null, CustomsTariffNumberCode: null,
CompanyCode: '1' CompanyCode: '1'
@@ -956,6 +1030,11 @@ function buildProductionUpdateMailPayload (selectedRows) {
async function sendUpdateMailAfterApply (selectedRows) { async function sendUpdateMailAfterApply (selectedRows) {
const orderId = String(orderHeaderID.value || '').trim() const orderId = String(orderHeaderID.value || '').trim()
if (!orderId) return if (!orderId) return
const host = String(window?.location?.hostname || '').trim().toLowerCase()
const isLocalHost = host === 'localhost' || host === '127.0.0.1'
if (isLocalHost) {
return
}
try { try {
const payload = buildProductionUpdateMailPayload(selectedRows) const payload = buildProductionUpdateMailPayload(selectedRows)
@@ -1082,12 +1161,14 @@ async function refreshAll () {
} }
async function onBulkSubmit () { async function onBulkSubmit () {
const flowStart = nowMs()
const selectedRows = rows.value.filter(r => !!selectedMap.value[r.RowKey]) const selectedRows = rows.value.filter(r => !!selectedMap.value[r.RowKey])
if (!selectedRows.length) { if (!selectedRows.length) {
$q.notify({ type: 'warning', message: 'Lutfen en az bir satir seciniz.' }) $q.notify({ type: 'warning', message: 'Lutfen en az bir satir seciniz.' })
return return
} }
const prepStart = nowMs()
const { errMsg, lines } = collectLinesFromRows(selectedRows) const { errMsg, lines } = collectLinesFromRows(selectedRows)
if (errMsg) { if (errMsg) {
$q.notify({ type: 'negative', message: errMsg }) $q.notify({ type: 'negative', message: errMsg })
@@ -1112,8 +1193,24 @@ async function onBulkSubmit () {
return return
} }
console.info('[OrderProductionUpdate] onBulkSubmit prepared', {
orderHeaderID: orderHeaderID.value,
selectedRowCount: selectedRows.length,
lineCount: lines.length,
cdItemCount: cdItems.length,
attributeCount: productAttributes.length,
prepDurationMs: Math.round(nowMs() - prepStart)
})
try { try {
const validateStart = nowMs()
const validate = await store.validateUpdates(orderHeaderID.value, lines) const validate = await store.validateUpdates(orderHeaderID.value, lines)
console.info('[OrderProductionUpdate] validate finished', {
orderHeaderID: orderHeaderID.value,
lineCount: lines.length,
missingCount: Number(validate?.missingCount || 0),
durationMs: Math.round(nowMs() - validateStart)
})
const missingCount = validate?.missingCount || 0 const missingCount = validate?.missingCount || 0
if (missingCount > 0) { if (missingCount > 0) {
const missingList = (validate?.missing || []).map(v => ( const missingList = (validate?.missing || []).map(v => (
@@ -1126,7 +1223,13 @@ async function onBulkSubmit () {
ok: { label: 'Ekle ve Guncelle', color: 'primary' }, ok: { label: 'Ekle ve Guncelle', color: 'primary' },
cancel: { label: 'Vazgec', flat: true } cancel: { label: 'Vazgec', flat: true }
}).onOk(async () => { }).onOk(async () => {
const applyStart = nowMs()
await store.applyUpdates(orderHeaderID.value, lines, true, cdItems, productAttributes) await store.applyUpdates(orderHeaderID.value, lines, true, cdItems, productAttributes)
console.info('[OrderProductionUpdate] apply finished', {
orderHeaderID: orderHeaderID.value,
insertMissing: true,
durationMs: Math.round(nowMs() - applyStart)
})
await store.fetchItems(orderHeaderID.value) await store.fetchItems(orderHeaderID.value)
selectedMap.value = {} selectedMap.value = {}
await sendUpdateMailAfterApply(selectedRows) await sendUpdateMailAfterApply(selectedRows)
@@ -1134,7 +1237,13 @@ async function onBulkSubmit () {
return return
} }
const applyStart = nowMs()
await store.applyUpdates(orderHeaderID.value, lines, false, cdItems, productAttributes) await store.applyUpdates(orderHeaderID.value, lines, false, cdItems, productAttributes)
console.info('[OrderProductionUpdate] apply finished', {
orderHeaderID: orderHeaderID.value,
insertMissing: false,
durationMs: Math.round(nowMs() - applyStart)
})
await store.fetchItems(orderHeaderID.value) await store.fetchItems(orderHeaderID.value)
selectedMap.value = {} selectedMap.value = {}
await sendUpdateMailAfterApply(selectedRows) await sendUpdateMailAfterApply(selectedRows)
@@ -1148,6 +1257,10 @@ async function onBulkSubmit () {
}) })
$q.notify({ type: 'negative', message: store.error || 'Toplu kayit islemi basarisiz.' }) $q.notify({ type: 'negative', message: store.error || 'Toplu kayit islemi basarisiz.' })
} }
console.info('[OrderProductionUpdate] onBulkSubmit total', {
orderHeaderID: orderHeaderID.value,
durationMs: Math.round(nowMs() - flowStart)
})
} }
</script> </script>

View File

@@ -29,13 +29,26 @@ function logApiError (action, err, payload = null) {
}) })
} }
function nowMs () {
if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
return performance.now()
}
return Date.now()
}
export const useOrderProductionItemStore = defineStore('orderproductionitems', { export const useOrderProductionItemStore = defineStore('orderproductionitems', {
state: () => ({ state: () => ({
items: [], items: [],
header: null, header: null,
products: [], products: [],
colorOptionsByCode: {}, colorOptionsByCode: {},
newColorOptionsByCode: {},
secondColorOptionsByKey: {}, secondColorOptionsByKey: {},
newSecondColorOptionsByKey: {},
colorRequestsByCode: {},
newColorRequestsByCode: {},
secondColorRequestsByKey: {},
newSecondColorRequestsByKey: {},
productAttributesByItemType: {}, productAttributesByItemType: {},
cdItemLookups: null, cdItemLookups: null,
cdItemDraftsByCode: {}, cdItemDraftsByCode: {},
@@ -127,16 +140,57 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
if (this.colorOptionsByCode[code]) { if (this.colorOptionsByCode[code]) {
return this.colorOptionsByCode[code] return this.colorOptionsByCode[code]
} }
if (this.colorRequestsByCode[code]) {
return this.colorRequestsByCode[code]
}
try { try {
const res = await api.get('/product-colors', { params: { code } }) this.colorRequestsByCode[code] = (async () => {
const data = res?.data const t0 = nowMs()
const list = Array.isArray(data) ? data : [] console.info('[OrderProductionItemStore] fetchColors start', { code })
this.colorOptionsByCode[code] = list const res = await api.get('/product-colors', { params: { code } })
return list const data = res?.data
const list = Array.isArray(data) ? data : []
this.colorOptionsByCode[code] = list
console.info('[OrderProductionItemStore] fetchColors done', { code, count: list.length, durationMs: Math.round(nowMs() - t0) })
return list
})()
return await this.colorRequestsByCode[code]
} catch (err) { } catch (err) {
this.error = err?.response?.data || err?.message || 'Renk listesi alinamadi' this.error = err?.response?.data || err?.message || 'Renk listesi alinamadi'
return [] return []
} finally {
delete this.colorRequestsByCode[code]
}
},
async fetchNewColors (productCode) {
const code = String(productCode || '').trim()
if (!code) return []
if (this.newColorOptionsByCode[code]) {
return this.newColorOptionsByCode[code]
}
if (this.newColorRequestsByCode[code]) {
return this.newColorRequestsByCode[code]
}
try {
this.newColorRequestsByCode[code] = (async () => {
const t0 = nowMs()
console.info('[OrderProductionItemStore] fetchNewColors start', { code })
const res = await api.get('/product-newcolors', { params: { code } })
const data = res?.data
const list = Array.isArray(data) ? data : []
this.newColorOptionsByCode[code] = list
console.info('[OrderProductionItemStore] fetchNewColors done', { code, count: list.length, durationMs: Math.round(nowMs() - t0) })
return list
})()
return await this.newColorRequestsByCode[code]
} catch (err) {
this.error = err?.response?.data || err?.message || 'Yeni urun renk listesi alinamadi'
return []
} finally {
delete this.newColorRequestsByCode[code]
} }
}, },
async fetchSecondColors (productCode, colorCode) { async fetchSecondColors (productCode, colorCode) {
@@ -148,16 +202,59 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
if (this.secondColorOptionsByKey[key]) { if (this.secondColorOptionsByKey[key]) {
return this.secondColorOptionsByKey[key] return this.secondColorOptionsByKey[key]
} }
if (this.secondColorRequestsByKey[key]) {
return this.secondColorRequestsByKey[key]
}
try { try {
const res = await api.get('/product-secondcolor', { params: { code, color } }) this.secondColorRequestsByKey[key] = (async () => {
const data = res?.data const t0 = nowMs()
const list = Array.isArray(data) ? data : [] console.info('[OrderProductionItemStore] fetchSecondColors start', { code, color })
this.secondColorOptionsByKey[key] = list const res = await api.get('/product-secondcolor', { params: { code, color } })
return list const data = res?.data
const list = Array.isArray(data) ? data : []
this.secondColorOptionsByKey[key] = list
console.info('[OrderProductionItemStore] fetchSecondColors done', { code, color, count: list.length, durationMs: Math.round(nowMs() - t0) })
return list
})()
return await this.secondColorRequestsByKey[key]
} catch (err) { } catch (err) {
this.error = err?.response?.data || err?.message || '2. renk listesi alinamadi' this.error = err?.response?.data || err?.message || '2. renk listesi alinamadi'
return [] return []
} finally {
delete this.secondColorRequestsByKey[key]
}
},
async fetchNewSecondColors (productCode, colorCode) {
const code = String(productCode || '').trim()
const color = String(colorCode || '').trim()
if (!code || !color) return []
const key = `${code}::${color}`
if (this.newSecondColorOptionsByKey[key]) {
return this.newSecondColorOptionsByKey[key]
}
if (this.newSecondColorRequestsByKey[key]) {
return this.newSecondColorRequestsByKey[key]
}
try {
this.newSecondColorRequestsByKey[key] = (async () => {
const t0 = nowMs()
console.info('[OrderProductionItemStore] fetchNewSecondColors start', { code, color })
const res = await api.get('/product-newsecondcolor', { params: { code, color } })
const data = res?.data
const list = Array.isArray(data) ? data : []
this.newSecondColorOptionsByKey[key] = list
console.info('[OrderProductionItemStore] fetchNewSecondColors done', { code, color, count: list.length, durationMs: Math.round(nowMs() - t0) })
return list
})()
return await this.newSecondColorRequestsByKey[key]
} catch (err) {
this.error = err?.response?.data || err?.message || 'Yeni urun 2. renk listesi alinamadi'
return []
} finally {
delete this.newSecondColorRequestsByKey[key]
} }
}, },
async fetchProductAttributes (itemTypeCode = 1) { async fetchProductAttributes (itemTypeCode = 1) {
@@ -231,11 +328,22 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
this.error = null this.error = null
try { try {
const t0 = nowMs()
console.info('[OrderProductionItemStore] validateUpdates start', { orderHeaderID, lineCount: lines?.length || 0 })
const res = await api.post( const res = await api.post(
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/validate`, `/orders/production-items/${encodeURIComponent(orderHeaderID)}/validate`,
{ lines } { lines }
) )
return res?.data || { missingCount: 0, missing: [] } const data = res?.data || { missingCount: 0, missing: [] }
const rid = res?.headers?.['x-debug-request-id'] || ''
console.info('[OrderProductionItemStore] validateUpdates done', {
orderHeaderID,
lineCount: lines?.length || 0,
missingCount: Number(data?.missingCount || 0),
requestId: rid,
durationMs: Math.round(nowMs() - t0)
})
return data
} catch (err) { } catch (err) {
logApiError('validateUpdates', err, { orderHeaderID, lineCount: lines?.length || 0 }) logApiError('validateUpdates', err, { orderHeaderID, lineCount: lines?.length || 0 })
this.error = extractApiErrorMessage(err, 'Kontrol basarisiz') this.error = extractApiErrorMessage(err, 'Kontrol basarisiz')
@@ -251,11 +359,29 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
this.error = null this.error = null
try { try {
const t0 = nowMs()
console.info('[OrderProductionItemStore] applyUpdates start', {
orderHeaderID,
lineCount: lines?.length || 0,
insertMissing: !!insertMissing,
cdItemCount: cdItems?.length || 0,
attributeCount: productAttributes?.length || 0
})
const res = await api.post( const res = await api.post(
`/orders/production-items/${encodeURIComponent(orderHeaderID)}/apply`, `/orders/production-items/${encodeURIComponent(orderHeaderID)}/apply`,
{ lines, insertMissing, cdItems, productAttributes } { lines, insertMissing, cdItems, productAttributes }
) )
return res?.data || { updated: 0, inserted: 0 } const data = res?.data || { updated: 0, inserted: 0 }
const rid = res?.headers?.['x-debug-request-id'] || ''
console.info('[OrderProductionItemStore] applyUpdates done', {
orderHeaderID,
updated: Number(data?.updated || 0),
inserted: Number(data?.inserted || 0),
attributeUpserted: Number(data?.attributeUpserted || 0),
requestId: rid,
durationMs: Math.round(nowMs() - t0)
})
return data
} catch (err) { } catch (err) {
logApiError('applyUpdates', err, { orderHeaderID, lineCount: lines?.length || 0, insertMissing }) logApiError('applyUpdates', err, { orderHeaderID, lineCount: lines?.length || 0, insertMissing })
this.error = extractApiErrorMessage(err, 'Guncelleme basarisiz') this.error = extractApiErrorMessage(err, 'Guncelleme basarisiz')