Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-03-31 12:45:22 +03:00
parent 44439f7908
commit d7d871fb8a
19 changed files with 1608 additions and 158 deletions

View File

@@ -522,6 +522,7 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
{"/api/order/get/{id}", "GET", "view", routes.GetOrderByIDHandler(mssql)},
{"/api/orders/list", "GET", "view", routes.OrderListRoute(mssql)},
{"/api/orders/production-list", "GET", "update", routes.OrderProductionListRoute(mssql)},
{"/api/orders/production-items/cditem-lookups", "GET", "view", routes.OrderProductionCdItemLookupsRoute(mssql)},
{"/api/orders/production-items/{id}", "GET", "view", routes.OrderProductionItemsRoute(mssql)},
{"/api/orders/production-items/{id}/insert-missing", "POST", "update", routes.OrderProductionInsertMissingRoute(mssql)},
{"/api/orders/production-items/{id}/validate", "POST", "update", routes.OrderProductionValidateRoute(mssql)},
@@ -587,6 +588,11 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router
"order", "view",
wrapV3(http.HandlerFunc(routes.GetProductSecondColorsHandler)),
)
bindV3(r, pgDB,
"/api/product-attributes", "GET",
"order", "view",
wrapV3(http.HandlerFunc(routes.GetProductAttributesHandler)),
)
bindV3(r, pgDB,
"/api/product-stock-query", "GET",
"order", "view",

View File

@@ -9,16 +9,72 @@ type OrderProductionUpdateLine struct {
}
type OrderProductionUpdatePayload struct {
Lines []OrderProductionUpdateLine `json:"lines"`
InsertMissing bool `json:"insertMissing"`
Lines []OrderProductionUpdateLine `json:"lines"`
InsertMissing bool `json:"insertMissing"`
CdItems []OrderProductionCdItemDraft `json:"cdItems"`
ProductAttributes []OrderProductionItemAttributeRow `json:"productAttributes"`
}
type OrderProductionMissingVariant struct {
OrderLineID string `json:"OrderLineID"`
ItemTypeCode int16 `json:"ItemTypeCode"`
ItemCode string `json:"ItemCode"`
ColorCode string `json:"ColorCode"`
ItemDim1Code string `json:"ItemDim1Code"`
ItemDim2Code string `json:"ItemDim2Code"`
ItemDim3Code string `json:"ItemDim3Code"`
OrderLineID string `json:"OrderLineID"`
ItemTypeCode int16 `json:"ItemTypeCode"`
ItemCode string `json:"ItemCode"`
ColorCode string `json:"ColorCode"`
ItemDim1Code string `json:"ItemDim1Code"`
ItemDim2Code string `json:"ItemDim2Code"`
ItemDim3Code string `json:"ItemDim3Code"`
}
type OrderProductionCdItemDraft struct {
ItemTypeCode int16 `json:"ItemTypeCode"`
ItemCode string `json:"ItemCode"`
ItemDimTypeCode *int16 `json:"ItemDimTypeCode"`
ProductTypeCode *int16 `json:"ProductTypeCode"`
ProductHierarchyID *int `json:"ProductHierarchyID"`
UnitOfMeasureCode1 *string `json:"UnitOfMeasureCode1"`
ItemAccountGrCode *string `json:"ItemAccountGrCode"`
ItemTaxGrCode *string `json:"ItemTaxGrCode"`
ItemPaymentPlanGrCode *string `json:"ItemPaymentPlanGrCode"`
ItemDiscountGrCode *string `json:"ItemDiscountGrCode"`
ItemVendorGrCode *string `json:"ItemVendorGrCode"`
PromotionGroupCode *string `json:"PromotionGroupCode"`
ProductCollectionGrCode *string `json:"ProductCollectionGrCode"`
StorePriceLevelCode *string `json:"StorePriceLevelCode"`
PerceptionOfFashionCode *string `json:"PerceptionOfFashionCode"`
CommercialRoleCode *string `json:"CommercialRoleCode"`
StoreCapacityLevelCode *string `json:"StoreCapacityLevelCode"`
CustomsTariffNumberCode *string `json:"CustomsTariffNumberCode"`
CompanyCode *string `json:"CompanyCode"`
}
type OrderProductionLookupOption struct {
Code string `json:"code"`
Description string `json:"description"`
}
type OrderProductionItemAttributeRow struct {
ItemTypeCode int16 `json:"ItemTypeCode"`
ItemCode string `json:"ItemCode"`
AttributeTypeCode int `json:"AttributeTypeCode"`
AttributeCode string `json:"AttributeCode"`
}
type OrderProductionCdItemLookups struct {
ItemDimTypeCodes []OrderProductionLookupOption `json:"itemDimTypeCodes"`
ProductTypeCodes []OrderProductionLookupOption `json:"productTypeCodes"`
ProductHierarchyIDs []OrderProductionLookupOption `json:"productHierarchyIDs"`
UnitOfMeasureCode1List []OrderProductionLookupOption `json:"unitOfMeasureCode1List"`
ItemAccountGrCodes []OrderProductionLookupOption `json:"itemAccountGrCodes"`
ItemTaxGrCodes []OrderProductionLookupOption `json:"itemTaxGrCodes"`
ItemPaymentPlanGrCodes []OrderProductionLookupOption `json:"itemPaymentPlanGrCodes"`
ItemDiscountGrCodes []OrderProductionLookupOption `json:"itemDiscountGrCodes"`
ItemVendorGrCodes []OrderProductionLookupOption `json:"itemVendorGrCodes"`
PromotionGroupCodes []OrderProductionLookupOption `json:"promotionGroupCodes"`
ProductCollectionGrCodes []OrderProductionLookupOption `json:"productCollectionGrCodes"`
StorePriceLevelCodes []OrderProductionLookupOption `json:"storePriceLevelCodes"`
PerceptionOfFashionCodes []OrderProductionLookupOption `json:"perceptionOfFashionCodes"`
CommercialRoleCodes []OrderProductionLookupOption `json:"commercialRoleCodes"`
StoreCapacityLevelCodes []OrderProductionLookupOption `json:"storeCapacityLevelCodes"`
CustomsTariffNumbers []OrderProductionLookupOption `json:"customsTariffNumbers"`
CompanyCodes []OrderProductionLookupOption `json:"companyCodes"`
}

View File

@@ -0,0 +1,9 @@
package models
type ProductAttributeOption struct {
ItemTypeCode int16 `json:"item_type_code"`
AttributeTypeCode int `json:"attribute_type_code"`
AttributeTypeDescription string `json:"attribute_type_description"`
AttributeCode string `json:"attribute_code"`
AttributeDescription string `json:"attribute_description"`
}

View File

@@ -1,7 +1,8 @@
package models
type ProductSecondColor struct {
ProductCode string `json:"product_code"`
ColorCode string `json:"color_code"`
ItemDim2Code string `json:"item_dim2_code"`
ProductCode string `json:"product_code"`
ColorCode string `json:"color_code"`
ItemDim2Code string `json:"item_dim2_code"`
ColorDescription string `json:"color_description"`
}

View File

@@ -3,6 +3,7 @@ package queries
import (
"database/sql"
"strconv"
"strings"
"bssapp-backend/models"
)
@@ -74,10 +75,17 @@ INSERT INTO dbo.prItemVariant (
ItemDim2Code,
ItemDim3Code,
PLU,
IsSalesOrderClosed,
IsPurchaseOrderClosed,
IsLocked,
IsBlocked,
CreatedUserName,
CreatedDate,
LastUpdatedUserName,
LastUpdatedDate
LastUpdatedDate,
RowGuid,
UseInternet,
IsStoreOrderClosed
)
SELECT
m.ItemTypeCode,
@@ -87,10 +95,17 @@ SELECT
m.ItemDim2Code,
m.ItemDim3Code,
mp.BasePlu + ROW_NUMBER() OVER (ORDER BY m.ItemCode, m.ColorCode, m.ItemDim1Code, m.ItemDim2Code, m.ItemDim3Code),
0,
0,
0,
0,
@p2,
GETDATE(),
@p2,
GETDATE()
GETDATE(),
NEWID(),
0,
0
FROM Missing m
CROSS JOIN MaxPlu mp;
`
@@ -143,7 +158,12 @@ WHERE ItemTypeCode = @p1
return true, nil
}
func InsertMissingVariantsTx(tx *sql.Tx, missing []models.OrderProductionMissingVariant, username string) (int64, error) {
func InsertMissingVariantsTx(
tx *sql.Tx,
missing []models.OrderProductionMissingVariant,
username string,
cdItemByCode map[string]models.OrderProductionCdItemDraft,
) (int64, error) {
if len(missing) == 0 {
return 0, nil
}
@@ -161,7 +181,16 @@ FROM dbo.prItemVariant WITH (UPDLOCK, HOLDLOCK)
for i, v := range missing {
itemKey := strconv.FormatInt(int64(v.ItemTypeCode), 10) + "|" + v.ItemCode
if _, ok := ensuredItems[itemKey]; !ok {
if err := ensureCdItemTx(tx, v.ItemTypeCode, v.ItemCode, username); err != nil {
draft, hasDraft := cdItemByCode[itemKey]
if !hasDraft {
draft, hasDraft = cdItemByCode[NormalizeCdItemMapKey(v.ItemTypeCode, v.ItemCode)]
}
var draftPtr *models.OrderProductionCdItemDraft
if hasDraft {
tmp := draft
draftPtr = &tmp
}
if err := ensureCdItemTx(tx, v.ItemTypeCode, v.ItemCode, username, draftPtr); err != nil {
return inserted, err
}
ensuredItems[itemKey] = struct{}{}
@@ -187,14 +216,26 @@ INSERT INTO dbo.prItemVariant (
ItemDim2Code,
ItemDim3Code,
PLU,
IsSalesOrderClosed,
IsPurchaseOrderClosed,
IsLocked,
IsBlocked,
CreatedUserName,
CreatedDate,
LastUpdatedUserName,
LastUpdatedDate
LastUpdatedDate,
RowGuid,
UseInternet,
IsStoreOrderClosed
)
VALUES (
@p1, @p2, @p3, @p4, @p5, @p6,
@p7, @p8, GETDATE(), @p8, GETDATE()
@p7,
0, 0, 0, 0,
@p8, GETDATE(), @p8, GETDATE(),
NEWID(),
0,
0
);
`, v.ItemTypeCode, v.ItemCode, v.ColorCode, v.ItemDim1Code, v.ItemDim2Code, v.ItemDim3Code, plu, username)
if err != nil {
@@ -207,7 +248,17 @@ VALUES (
return inserted, nil
}
func ensureCdItemTx(tx *sql.Tx, itemTypeCode int16, itemCode string, username string) error {
func NormalizeCdItemMapKey(itemTypeCode int16, itemCode string) string {
return strconv.FormatInt(int64(itemTypeCode), 10) + "|" + strings.ToUpper(strings.TrimSpace(itemCode))
}
func ensureCdItemTx(
tx *sql.Tx,
itemTypeCode int16,
itemCode string,
username string,
draft *models.OrderProductionCdItemDraft,
) error {
_, err := tx.Exec(`
IF NOT EXISTS (
SELECT 1
@@ -269,28 +320,90 @@ BEGIN
INSERT INTO dbo.cdItem (
ItemTypeCode, ItemCode,
ItemDimTypeCode, ProductTypeCode, ProductHierarchyID,
UnitOfMeasureCode1, UnitConvertRate, UnitConvertRateNotFixed,
UnitOfMeasureCode1, UnitOfMeasureCode2, UnitConvertRate, UnitConvertRateNotFixed,
UseInternet, UsePOS, UseStore, EnablePartnerCompanies, UseManufacturing, UseSerialNumber,
GenerateOpticalDataMatrixCode, ByWeight, SupplyPeriod, GuaranteePeriod, ShelfLife, OrderLeadTime,
IsFixedExpense, IsBlocked, IsLocked, LockedDate, IsSalesOrderClosed, IsPurchaseOrderClosed,
ItemAccountGrCode, ItemTaxGrCode, ItemPaymentPlanGrCode, ItemDiscountGrCode, ItemVendorGrCode,
PromotionGroupCode, PromotionGroupCode2, ProductCollectionGrCode, StorePriceLevelCode, PerceptionOfFashionCode,
CommercialRoleCode, StoreCapacityLevelCode, CustomsTariffNumberCode, IsFixedExpense, BOMEntityCode, CompanyCode,
IsBlocked, IsLocked, LockedDate, IsSalesOrderClosed, IsPurchaseOrderClosed,
CreatedUserName, CreatedDate, LastUpdatedUserName, LastUpdatedDate, RowGuid,
UseRoll, UseBatch, MaxCreditCardInstallmentCount, GenerateSerialNumber,
IsSubsequentDeliveryForR, IsSubsequentDeliveryForRI, IsUTSDeclaratedItem, IsStoreOrderClosed
IsSubsequentDeliveryForR, IsSubsequentDeliveryForRI,
IGACommissionGroup, UniFreeCommissionGroup, CustomsProductGroupCode, IsUTSDeclaratedItem, IsStoreOrderClosed
)
VALUES (
@p1, @p2,
2, 1, 2,
'AD', 0, 0,
0, 1, 1, 0, 1, 0,
'AD', '', 0, 0,
1, 1, 1, 0, 1, 0,
0, 0, 0, 0, 0, 0,
0, 0, 0, '1900-01-01', 0, 0,
'', '10%', '', '', '',
'', '', '0', '0', '0',
'0', '', '', 0, '', '1',
0, 0, '1900-01-01', 0, 0,
@p3, GETDATE(), @p3, GETDATE(), NEWID(),
0, 0, 12, 0,
0, 0, 0, 0
0, 0,
'', '', '0', 0, 0
);
END
END
`, itemTypeCode, itemCode, username)
if err != nil {
return err
}
if draft == nil {
return nil
}
_, err = tx.Exec(`
UPDATE dbo.cdItem
SET
ItemDimTypeCode = COALESCE(@p3, ItemDimTypeCode),
ProductTypeCode = COALESCE(@p4, ProductTypeCode),
ProductHierarchyID = COALESCE(@p5, ProductHierarchyID),
UnitOfMeasureCode1 = COALESCE(NULLIF(@p6,''), UnitOfMeasureCode1),
ItemAccountGrCode = COALESCE(NULLIF(@p7,''), ItemAccountGrCode),
ItemTaxGrCode = COALESCE(NULLIF(@p8,''), ItemTaxGrCode),
ItemPaymentPlanGrCode = COALESCE(NULLIF(@p9,''), ItemPaymentPlanGrCode),
ItemDiscountGrCode = COALESCE(NULLIF(@p10,''), ItemDiscountGrCode),
ItemVendorGrCode = COALESCE(NULLIF(@p11,''), ItemVendorGrCode),
PromotionGroupCode = COALESCE(NULLIF(@p12,''), PromotionGroupCode),
ProductCollectionGrCode = COALESCE(NULLIF(@p13,''), ProductCollectionGrCode),
StorePriceLevelCode = COALESCE(NULLIF(@p14,''), StorePriceLevelCode),
PerceptionOfFashionCode = COALESCE(NULLIF(@p15,''), PerceptionOfFashionCode),
CommercialRoleCode = COALESCE(NULLIF(@p16,''), CommercialRoleCode),
StoreCapacityLevelCode = COALESCE(NULLIF(@p17,''), StoreCapacityLevelCode),
CustomsTariffNumberCode = COALESCE(NULLIF(@p18,''), CustomsTariffNumberCode),
CompanyCode = COALESCE(NULLIF(@p19,''), CompanyCode),
LastUpdatedUserName = @p20,
LastUpdatedDate = GETDATE()
WHERE ItemTypeCode = @p1
AND ItemCode = @p2;
`,
itemTypeCode,
itemCode,
draft.ItemDimTypeCode,
draft.ProductTypeCode,
draft.ProductHierarchyID,
draft.UnitOfMeasureCode1,
draft.ItemAccountGrCode,
draft.ItemTaxGrCode,
draft.ItemPaymentPlanGrCode,
draft.ItemDiscountGrCode,
draft.ItemVendorGrCode,
draft.PromotionGroupCode,
draft.ProductCollectionGrCode,
draft.StorePriceLevelCode,
draft.PerceptionOfFashionCode,
draft.CommercialRoleCode,
draft.StoreCapacityLevelCode,
draft.CustomsTariffNumberCode,
draft.CompanyCode,
username,
)
return err
}
@@ -317,3 +430,141 @@ WHERE OrderHeaderID = @p6 AND OrderLineID = @p7
}
return updated, nil
}
func UpsertItemAttributesTx(tx *sql.Tx, attrs []models.OrderProductionItemAttributeRow, username string) (int64, error) {
if len(attrs) == 0 {
return 0, nil
}
var affected int64
for _, a := range attrs {
res, err := tx.Exec(`
IF EXISTS (
SELECT 1
FROM dbo.prItemAttribute
WHERE ItemTypeCode = @p1
AND ItemCode = @p2
AND AttributeTypeCode = @p3
)
BEGIN
UPDATE dbo.prItemAttribute
SET
AttributeCode = @p4,
LastUpdatedUserName = @p5,
LastUpdatedDate = GETDATE()
WHERE ItemTypeCode = @p1
AND ItemCode = @p2
AND AttributeTypeCode = @p3
END
ELSE
BEGIN
INSERT INTO dbo.prItemAttribute (
ItemTypeCode,
ItemCode,
AttributeTypeCode,
AttributeCode,
CreatedUserName,
CreatedDate,
LastUpdatedUserName,
LastUpdatedDate,
RowGuid
)
VALUES (
@p1,
@p2,
@p3,
@p4,
@p5,
GETDATE(),
@p5,
GETDATE(),
NEWID()
)
END
`, a.ItemTypeCode, a.ItemCode, a.AttributeTypeCode, a.AttributeCode, username)
if err != nil {
return affected, err
}
if rows, err := res.RowsAffected(); err == nil {
affected += rows
}
}
return affected, nil
}
func GetOrderProductionLookupOptions(mssql *sql.DB) (models.OrderProductionCdItemLookups, error) {
out := models.OrderProductionCdItemLookups{}
queryPairs := []struct {
Query string
Target *[]models.OrderProductionLookupOption
}{
{`SELECT
CAST(t.ItemDimTypeCode AS NVARCHAR(50)) AS Code,
ISNULL(d.ItemDimTypeDescription, CAST(t.ItemDimTypeCode AS NVARCHAR(50))) AS [Description]
FROM dbo.bsItemDimType t WITH(NOLOCK)
LEFT JOIN dbo.bsItemDimTypeDesc d WITH(NOLOCK)
ON d.ItemDimTypeCode = t.ItemDimTypeCode
AND d.LangCode = 'TR'
WHERE ISNULL(t.IsBlocked, 0) = 0
ORDER BY t.ItemDimTypeCode`, &out.ItemDimTypeCodes},
{`SELECT DISTINCT CAST(ProductTypeCode AS NVARCHAR(50)) AS Code, CAST(ProductTypeCode AS NVARCHAR(50)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ProductTypeCode IS NOT NULL ORDER BY Code`, &out.ProductTypeCodes},
{`SELECT
CAST(h.ProductHierarchyID AS NVARCHAR(50)) AS Code,
LTRIM(RTRIM(
CONCAT(
CAST(ISNULL(h.ProductHierarchyLevelCode01, 0) AS NVARCHAR(50)),
CASE
WHEN ISNULL(d.ProductHierarchyLevelDescription, '') <> '' THEN CONCAT(' - ', d.ProductHierarchyLevelDescription)
ELSE ''
END
)
)) AS [Description]
FROM dbo.dfProductHierarchy h WITH(NOLOCK)
LEFT JOIN dbo.cdProductHierarchyLevelDesc d WITH(NOLOCK)
ON d.ProductHierarchyLevelCode = h.ProductHierarchyLevelCode01
AND d.LangCode = 'TR'
ORDER BY h.ProductHierarchyID`, &out.ProductHierarchyIDs},
{`SELECT DISTINCT CAST(UnitOfMeasureCode1 AS NVARCHAR(50)) AS Code, CAST(UnitOfMeasureCode1 AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(UnitOfMeasureCode1,'') <> '' ORDER BY Code`, &out.UnitOfMeasureCode1List},
{`SELECT DISTINCT CAST(ItemAccountGrCode AS NVARCHAR(50)) AS Code, CAST(ItemAccountGrCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(ItemAccountGrCode,'') <> '' ORDER BY Code`, &out.ItemAccountGrCodes},
{`SELECT DISTINCT CAST(ItemTaxGrCode AS NVARCHAR(50)) AS Code, CAST(ItemTaxGrCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(ItemTaxGrCode,'') <> '' ORDER BY Code`, &out.ItemTaxGrCodes},
{`SELECT DISTINCT CAST(ItemPaymentPlanGrCode AS NVARCHAR(50)) AS Code, CAST(ItemPaymentPlanGrCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(ItemPaymentPlanGrCode,'') <> '' ORDER BY Code`, &out.ItemPaymentPlanGrCodes},
{`SELECT DISTINCT CAST(ItemDiscountGrCode AS NVARCHAR(50)) AS Code, CAST(ItemDiscountGrCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(ItemDiscountGrCode,'') <> '' ORDER BY Code`, &out.ItemDiscountGrCodes},
{`SELECT DISTINCT CAST(ItemVendorGrCode AS NVARCHAR(50)) AS Code, CAST(ItemVendorGrCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(ItemVendorGrCode,'') <> '' ORDER BY Code`, &out.ItemVendorGrCodes},
{`SELECT DISTINCT CAST(PromotionGroupCode AS NVARCHAR(50)) AS Code, CAST(PromotionGroupCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(PromotionGroupCode,'') <> '' ORDER BY Code`, &out.PromotionGroupCodes},
{`SELECT DISTINCT CAST(ProductCollectionGrCode AS NVARCHAR(50)) AS Code, CAST(ProductCollectionGrCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(ProductCollectionGrCode,'') <> '' ORDER BY Code`, &out.ProductCollectionGrCodes},
{`SELECT DISTINCT CAST(StorePriceLevelCode AS NVARCHAR(50)) AS Code, CAST(StorePriceLevelCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(StorePriceLevelCode,'') <> '' ORDER BY Code`, &out.StorePriceLevelCodes},
{`SELECT DISTINCT CAST(PerceptionOfFashionCode AS NVARCHAR(50)) AS Code, CAST(PerceptionOfFashionCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(PerceptionOfFashionCode,'') <> '' ORDER BY Code`, &out.PerceptionOfFashionCodes},
{`SELECT DISTINCT CAST(CommercialRoleCode AS NVARCHAR(50)) AS Code, CAST(CommercialRoleCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(CommercialRoleCode,'') <> '' ORDER BY Code`, &out.CommercialRoleCodes},
{`SELECT DISTINCT CAST(StoreCapacityLevelCode AS NVARCHAR(50)) AS Code, CAST(StoreCapacityLevelCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(StoreCapacityLevelCode,'') <> '' ORDER BY Code`, &out.StoreCapacityLevelCodes},
{`SELECT DISTINCT CAST(CustomsTariffNumberCode AS NVARCHAR(50)) AS Code, CAST(CustomsTariffNumberCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(CustomsTariffNumberCode,'') <> '' ORDER BY Code`, &out.CustomsTariffNumbers},
{`SELECT DISTINCT CAST(CompanyCode AS NVARCHAR(50)) AS Code, CAST(CompanyCode AS NVARCHAR(200)) AS [Description] FROM dbo.cdItem WITH(NOLOCK) WHERE ISNULL(CompanyCode,'') <> '' ORDER BY Code`, &out.CompanyCodes},
}
for _, pair := range queryPairs {
rows, err := mssql.Query(pair.Query)
if err != nil {
return out, err
}
list := make([]models.OrderProductionLookupOption, 0, 64)
for rows.Next() {
var item models.OrderProductionLookupOption
if err := rows.Scan(&item.Code, &item.Description); err != nil {
rows.Close()
return out, err
}
item.Code = strings.TrimSpace(item.Code)
item.Description = strings.TrimSpace(item.Description)
list = append(list, item)
}
if err := rows.Err(); err != nil {
rows.Close()
return out, err
}
rows.Close()
*pair.Target = list
}
return out, nil
}

View File

@@ -0,0 +1,42 @@
package queries
const GetProductAttributes = `
;WITH TypeDesc AS (
SELECT
t.ItemTypeCode,
t.AttributeTypeCode,
ISNULL(t.AttributeTypeDescription, CAST(t.AttributeTypeCode AS NVARCHAR(30))) AS AttributeTypeDescription
FROM dbo.cdItemAttributeTypeDesc AS t WITH(NOLOCK)
WHERE t.ItemTypeCode = @p1
AND t.LangCode = 'TR'
),
Attr AS (
SELECT
a.ItemTypeCode,
a.AttributeTypeCode,
ISNULL(a.AttributeCode, '') AS AttributeCode,
ISNULL(d.AttributeDescription, ISNULL(a.AttributeCode, '')) AS AttributeDescription
FROM dbo.cdItemAttribute AS a WITH(NOLOCK)
LEFT JOIN dbo.cdItemAttributeDesc AS d WITH(NOLOCK)
ON d.ItemTypeCode = a.ItemTypeCode
AND d.AttributeTypeCode = a.AttributeTypeCode
AND d.AttributeCode = a.AttributeCode
AND d.LangCode = 'TR'
WHERE a.ItemTypeCode = @p1
AND ISNULL(a.IsBlocked, 0) = 0
),
SELECT
a.ItemTypeCode,
a.AttributeTypeCode,
ISNULL(NULLIF(td.AttributeTypeDescription, ''), CAST(a.AttributeTypeCode AS NVARCHAR(30))) AS AttributeTypeDescription,
a.AttributeCode,
a.AttributeDescription
FROM Attr a
LEFT JOIN TypeDesc td
ON td.ItemTypeCode = a.ItemTypeCode
AND td.AttributeTypeCode = a.AttributeTypeCode
ORDER BY
a.AttributeTypeCode,
CASE WHEN a.AttributeCode = '-' THEN 0 ELSE 1 END,
a.AttributeCode;
`

View File

@@ -4,7 +4,8 @@ const GetProductSecondColors = `
SELECT DISTINCT
Product.ProductCode,
ISNULL(prItemVariant.ColorCode, '') AS ColorCode,
ISNULL(prItemVariant.ItemDim2Code, '') AS ItemDim2Code
ISNULL(prItemVariant.ItemDim2Code, '') AS ItemDim2Code,
ISNULL(ColorDesc.ColorDescription, '') AS ColorDescription
FROM prItemVariant WITH(NOLOCK)
INNER JOIN ProductFilterWithDescription('TR') AS Product
ON prItemVariant.ItemCode = Product.ProductCode
@@ -14,5 +15,10 @@ FROM prItemVariant WITH(NOLOCK)
WHERE Product.ProductCode = @ProductCode
AND prItemVariant.ColorCode = @ColorCode
AND ISNULL(prItemVariant.ItemDim2Code, '') <> ''
GROUP BY Product.ProductCode, prItemVariant.ItemDim2Code, prItemVariant.ColorCode
GROUP BY
Product.ProductCode,
prItemVariant.ItemDim2Code,
prItemVariant.ColorCode,
ColorDesc.ColorDescription
ORDER BY prItemVariant.ItemDim2Code
`

View File

@@ -1597,6 +1597,17 @@ func renderOrderGrid(pdf *gofpdf.Fpdf, header *OrderHeader, rows []PdfRow, hasVa
layout := newPdfLayout(pdf)
catSizes := buildCategorySizeMap(rows)
normalizeYetiskinGarsonTokenGo := func(v string) string {
s := strings.ToUpper(strings.TrimSpace(v))
if strings.Contains(s, "GARSON") {
return "GARSON"
}
if strings.Contains(s, "YETISKIN") || strings.Contains(s, "YETİSKİN") {
return "YETISKIN"
}
return "GENEL"
}
// Grup: ÜRÜN ANA GRUBU
type group struct {
Name string
@@ -1609,15 +1620,19 @@ func renderOrderGrid(pdf *gofpdf.Fpdf, header *OrderHeader, rows []PdfRow, hasVa
var order []string
for _, r := range rows {
name := strings.TrimSpace(r.GroupMain)
if name == "" {
name = "GENEL"
ana := strings.TrimSpace(r.GroupMain)
if ana == "" {
ana = "GENEL"
}
g, ok := groups[name]
yg := normalizeYetiskinGarsonTokenGo(r.YetiskinGarson)
name := strings.TrimSpace(fmt.Sprintf("%s %s", yg, ana))
groupKey := fmt.Sprintf("%s::%s", yg, ana)
g, ok := groups[groupKey]
if !ok {
g = &group{Name: name}
groups[name] = g
order = append(order, name)
groups[groupKey] = g
order = append(order, groupKey)
}
g.Rows = append(g.Rows, r)
g.Adet += r.TotalQty
@@ -1673,8 +1688,8 @@ func renderOrderGrid(pdf *gofpdf.Fpdf, header *OrderHeader, rows []PdfRow, hasVa
newPage(firstPage, true)
firstPage = false
for _, name := range order {
g := groups[name]
for _, key := range order {
g := groups[key]
for _, row := range g.Rows {
rh := calcRowHeight(pdf, layout, row)

View File

@@ -9,12 +9,15 @@ import (
"errors"
"log"
"net/http"
"regexp"
"strings"
"github.com/gorilla/mux"
mssql "github.com/microsoft/go-mssqldb"
)
var baggiModelCodeRegex = regexp.MustCompile(`^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$`)
// ======================================================
// 📌 OrderProductionItemsRoute — U ürün satırları
// ======================================================
@@ -73,6 +76,23 @@ func OrderProductionItemsRoute(mssql *sql.DB) http.Handler {
})
}
func OrderProductionCdItemLookupsRoute(mssql *sql.DB) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=utf-8")
lookups, err := queries.GetOrderProductionLookupOptions(mssql)
if err != nil {
log.Printf("[OrderProductionCdItemLookupsRoute] lookup error: %v", err)
http.Error(w, "Veritabani hatasi", http.StatusInternalServerError)
return
}
if err := json.NewEncoder(w).Encode(lookups); err != nil {
log.Printf("[OrderProductionCdItemLookupsRoute] encode error: %v", err)
}
})
}
// ======================================================
// 📌 OrderProductionInsertMissingRoute — eksik varyantları ekler
// ======================================================
@@ -208,13 +228,24 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
var inserted int64
if payload.InsertMissing {
inserted, err = queries.InsertMissingVariantsTx(tx, missing, username)
cdItemByCode := buildCdItemDraftMap(payload.CdItems)
inserted, err = queries.InsertMissingVariantsTx(tx, missing, username, cdItemByCode)
if err != nil {
writeDBError(w, http.StatusInternalServerError, "insert_missing_variants", id, username, len(missing), err)
return
}
}
if err := validateProductAttributes(payload.ProductAttributes); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
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
}
updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username)
if err != nil {
writeDBError(w, http.StatusInternalServerError, "update_order_lines", id, username, len(payload.Lines), err)
@@ -227,8 +258,9 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
}
resp := map[string]any{
"updated": updated,
"inserted": inserted,
"updated": updated,
"inserted": inserted,
"attributeUpserted": attributeAffected,
}
if err := json.NewEncoder(w).Encode(resp); err != nil {
log.Printf("❌ encode error: %v", err)
@@ -236,6 +268,44 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
})
}
func validateProductAttributes(attrs []models.OrderProductionItemAttributeRow) error {
for _, a := range attrs {
if strings.TrimSpace(a.ItemCode) == "" {
return errors.New("Urun ozellikleri icin ItemCode zorunlu")
}
if !baggiModelCodeRegex.MatchString(strings.ToUpper(strings.TrimSpace(a.ItemCode))) {
return errors.New("Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999")
}
if a.ItemTypeCode <= 0 {
return errors.New("Urun ozellikleri icin ItemTypeCode zorunlu")
}
if a.AttributeTypeCode <= 0 {
return errors.New("Urun ozellikleri icin AttributeTypeCode zorunlu")
}
if strings.TrimSpace(a.AttributeCode) == "" {
return errors.New("Urun ozellikleri icin AttributeCode zorunlu")
}
}
return nil
}
func buildCdItemDraftMap(list []models.OrderProductionCdItemDraft) map[string]models.OrderProductionCdItemDraft {
out := make(map[string]models.OrderProductionCdItemDraft, len(list))
for _, item := range list {
code := strings.ToUpper(strings.TrimSpace(item.ItemCode))
if code == "" {
continue
}
item.ItemCode = code
if item.ItemTypeCode == 0 {
item.ItemTypeCode = 1
}
key := queries.NormalizeCdItemMapKey(item.ItemTypeCode, item.ItemCode)
out[key] = item
}
return out
}
func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) {
missing := make([]models.OrderProductionMissingVariant, 0)
@@ -279,9 +349,13 @@ func validateUpdateLines(lines []models.OrderProductionUpdateLine) error {
if strings.TrimSpace(line.OrderLineID) == "" {
return errors.New("OrderLineID zorunlu")
}
if strings.TrimSpace(line.NewItemCode) == "" {
code := strings.ToUpper(strings.TrimSpace(line.NewItemCode))
if code == "" {
return errors.New("Yeni urun kodu zorunlu")
}
if !baggiModelCodeRegex.MatchString(code) {
return errors.New("Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999")
}
}
return nil
}

View File

@@ -0,0 +1,54 @@
package routes
import (
"bssapp-backend/auth"
"bssapp-backend/db"
"bssapp-backend/models"
"bssapp-backend/queries"
"encoding/json"
"net/http"
"strconv"
)
func GetProductAttributesHandler(w http.ResponseWriter, r *http.Request) {
claims, ok := auth.GetClaimsFromContext(r.Context())
if !ok || claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
itemTypeCode := int16(1)
if raw := r.URL.Query().Get("itemTypeCode"); raw != "" {
v, err := strconv.Atoi(raw)
if err != nil || v <= 0 {
http.Error(w, "itemTypeCode gecersiz", http.StatusBadRequest)
return
}
itemTypeCode = int16(v)
}
rows, err := db.MssqlDB.Query(queries.GetProductAttributes, itemTypeCode)
if err != nil {
http.Error(w, "Product attributes alinamadi: "+err.Error(), http.StatusInternalServerError)
return
}
defer rows.Close()
list := make([]models.ProductAttributeOption, 0, 256)
for rows.Next() {
var x models.ProductAttributeOption
if err := rows.Scan(
&x.ItemTypeCode,
&x.AttributeTypeCode,
&x.AttributeTypeDescription,
&x.AttributeCode,
&x.AttributeDescription,
); err != nil {
continue
}
list = append(list, x)
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
_ = json.NewEncoder(w).Encode(list)
}

View File

@@ -45,7 +45,7 @@ func GetProductSecondColorsHandler(w http.ResponseWriter, r *http.Request) {
var list []models.ProductSecondColor
for rows.Next() {
var c models.ProductSecondColor
if err := rows.Scan(&c.ProductCode, &c.ColorCode, &c.ItemDim2Code); err != nil {
if err := rows.Scan(&c.ProductCode, &c.ColorCode, &c.ItemDim2Code, &c.ColorDescription); err != nil {
log.Println("⚠️ Satır okunamadı:", err)
continue
}