Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -25,14 +26,24 @@ SELECT
|
||||
|
||||
ISNULL(l.ItemCode,'') AS OldItemCode,
|
||||
ISNULL(l.ColorCode,'') AS OldColor,
|
||||
ISNULL((
|
||||
SELECT TOP 1 LTRIM(RTRIM(cd.ColorDescription))
|
||||
FROM dbo.cdColorDesc cd WITH (NOLOCK)
|
||||
WHERE cd.ColorCode = l.ColorCode
|
||||
AND cd.LangCode = N'TR'
|
||||
), '') AS OldColorDescription,
|
||||
ISNULL(l.ItemDim2Code,'') AS OldDim2,
|
||||
ISNULL(l.LineDescription,'') AS OldDesc,
|
||||
CAST(ISNULL(l.Qty1, 0) AS FLOAT) AS OldQty,
|
||||
|
||||
CAST('' AS NVARCHAR(60)) AS NewItemCode,
|
||||
CAST('' AS NVARCHAR(30)) AS NewColor,
|
||||
CAST('' AS NVARCHAR(30)) AS NewDim2,
|
||||
CAST('' AS NVARCHAR(250)) AS NewDesc,
|
||||
|
||||
CONVERT(NVARCHAR(10), l.DeliveryDate, 126) AS OldDueDate,
|
||||
CONVERT(NVARCHAR(10), l.DeliveryDate, 126) AS NewDueDate,
|
||||
|
||||
CAST(0 AS bit) AS IsVariantMissing
|
||||
FROM dbo.trOrderLine l
|
||||
WHERE l.OrderHeaderID = @p1
|
||||
@@ -522,18 +533,25 @@ func UpdateOrderLinesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderPr
|
||||
chunk := lines[i:end]
|
||||
|
||||
values := make([]string, 0, len(chunk))
|
||||
args := make([]any, 0, len(chunk)*5+2)
|
||||
args := make([]any, 0, len(chunk)*8+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))
|
||||
var itemDim1 any
|
||||
if line.ItemDim1Code != nil {
|
||||
itemDim1 = strings.TrimSpace(*line.ItemDim1Code)
|
||||
}
|
||||
values = append(values, fmt.Sprintf("(@p%d,@p%d,@p%d,@p%d,@p%d,@p%d,@p%d,@p%d)", paramPos, paramPos+1, paramPos+2, paramPos+3, paramPos+4, paramPos+5, paramPos+6, paramPos+7))
|
||||
args = append(args,
|
||||
strings.TrimSpace(line.OrderLineID),
|
||||
line.NewItemCode,
|
||||
line.NewColor,
|
||||
itemDim1,
|
||||
line.NewDim2,
|
||||
line.NewDesc,
|
||||
line.OldDueDate,
|
||||
line.NewDueDate,
|
||||
)
|
||||
paramPos += 5
|
||||
paramPos += 8
|
||||
}
|
||||
|
||||
orderHeaderParam := paramPos
|
||||
@@ -542,16 +560,18 @@ func UpdateOrderLinesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderPr
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SET NOCOUNT ON;
|
||||
WITH src (OrderLineID, NewItemCode, NewColor, NewDim2, NewDesc) AS (
|
||||
WITH src (OrderLineID, NewItemCode, NewColor, ItemDim1Code, NewDim2, NewDesc, OldDueDate, NewDueDate) AS (
|
||||
SELECT *
|
||||
FROM (VALUES %s) AS v (OrderLineID, NewItemCode, NewColor, NewDim2, NewDesc)
|
||||
FROM (VALUES %s) AS v (OrderLineID, NewItemCode, NewColor, ItemDim1Code, NewDim2, NewDesc, OldDueDate, NewDueDate)
|
||||
)
|
||||
UPDATE l
|
||||
SET
|
||||
l.ItemCode = s.NewItemCode,
|
||||
l.ColorCode = s.NewColor,
|
||||
l.ItemDim1Code = COALESCE(s.ItemDim1Code, l.ItemDim1Code),
|
||||
l.ItemDim2Code = s.NewDim2,
|
||||
l.LineDescription = COALESCE(NULLIF(s.NewDesc,''), l.LineDescription),
|
||||
l.DeliveryDate = CASE WHEN ISDATE(s.NewDueDate) = 1 THEN CAST(s.NewDueDate AS DATETIME) ELSE l.DeliveryDate END,
|
||||
l.LastUpdatedUserName = @p%d,
|
||||
l.LastUpdatedDate = GETDATE()
|
||||
FROM dbo.trOrderLine l
|
||||
@@ -574,6 +594,344 @@ WHERE l.OrderHeaderID = @p%d;
|
||||
return updated, nil
|
||||
}
|
||||
|
||||
func UpdateOrderHeaderAverageDueDateTx(tx *sql.Tx, orderHeaderID string, averageDueDate *string, username string) error {
|
||||
if averageDueDate == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
dueDate := strings.TrimSpace(*averageDueDate)
|
||||
if dueDate != "" {
|
||||
if _, err := time.Parse("2006-01-02", dueDate); err != nil {
|
||||
return fmt.Errorf("invalid header average due date %q: %w", dueDate, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := tx.Exec(`
|
||||
UPDATE dbo.trOrderHeader
|
||||
SET
|
||||
AverageDueDate = CASE WHEN @p1 = '' THEN NULL ELSE CAST(@p1 AS DATETIME) END,
|
||||
LastUpdatedUserName = @p2,
|
||||
LastUpdatedDate = GETDATE()
|
||||
WHERE OrderHeaderID = @p3;
|
||||
`, dueDate, username, orderHeaderID)
|
||||
return err
|
||||
}
|
||||
|
||||
type sqlQueryRower interface {
|
||||
QueryRow(query string, args ...any) *sql.Row
|
||||
}
|
||||
|
||||
type plannedProductionBarcode struct {
|
||||
Barcode string
|
||||
BarcodeTypeCode string
|
||||
ItemTypeCode int16
|
||||
ItemCode string
|
||||
ColorCode string
|
||||
ItemDim1Code string
|
||||
ItemDim2Code string
|
||||
ItemDim3Code string
|
||||
}
|
||||
|
||||
func barcodeTypeExists(q sqlQueryRower, barcodeTypeCode string) (bool, error) {
|
||||
var exists int
|
||||
err := q.QueryRow(`
|
||||
SELECT TOP 1 1
|
||||
FROM dbo.cdBarcodeType
|
||||
WHERE BarcodeTypeCode = @p1
|
||||
`, strings.TrimSpace(barcodeTypeCode)).Scan(&exists)
|
||||
if err == sql.ErrNoRows {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func barcodeExists(q sqlQueryRower, barcode string) (bool, error) {
|
||||
var exists int
|
||||
err := q.QueryRow(`
|
||||
SELECT TOP 1 1
|
||||
FROM dbo.prItemBarcode WITH (UPDLOCK, HOLDLOCK)
|
||||
WHERE Barcode = @p1
|
||||
`, strings.TrimSpace(barcode)).Scan(&exists)
|
||||
if err == sql.ErrNoRows {
|
||||
return false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func existingVariantBarcode(q sqlQueryRower, barcodeTypeCode string, itemTypeCode int16, itemCode string, colorCode string, dim1 string, dim2 string, dim3 string) (string, bool, error) {
|
||||
var barcode string
|
||||
err := q.QueryRow(`
|
||||
SELECT TOP 1 LTRIM(RTRIM(ISNULL(Barcode, '')))
|
||||
FROM dbo.prItemBarcode WITH (UPDLOCK, HOLDLOCK)
|
||||
WHERE BarcodeTypeCode = @p1
|
||||
AND ItemTypeCode = @p2
|
||||
AND ISNULL(LTRIM(RTRIM(ItemCode)), '') = @p3
|
||||
AND ISNULL(LTRIM(RTRIM(ColorCode)), '') = @p4
|
||||
AND ISNULL(LTRIM(RTRIM(ItemDim1Code)), '') = @p5
|
||||
AND ISNULL(LTRIM(RTRIM(ItemDim2Code)), '') = @p6
|
||||
AND ISNULL(LTRIM(RTRIM(ItemDim3Code)), '') = @p7
|
||||
AND ISNULL(LTRIM(RTRIM(UnitOfMeasureCode)), '') = 'AD'
|
||||
ORDER BY TRY_CONVERT(BIGINT, NULLIF(LTRIM(RTRIM(Barcode)), '')) DESC, Barcode DESC
|
||||
`, strings.TrimSpace(barcodeTypeCode), itemTypeCode, strings.TrimSpace(itemCode), strings.TrimSpace(colorCode), strings.TrimSpace(dim1), strings.TrimSpace(dim2), strings.TrimSpace(dim3)).Scan(&barcode)
|
||||
if err == sql.ErrNoRows {
|
||||
return "", false, nil
|
||||
}
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
return strings.TrimSpace(barcode), true, nil
|
||||
}
|
||||
|
||||
func maxNumericBarcode(q sqlQueryRower) (int64, error) {
|
||||
var maxBarcode int64
|
||||
err := q.QueryRow(`
|
||||
SELECT ISNULL(MAX(TRY_CONVERT(BIGINT, NULLIF(LTRIM(RTRIM(Barcode)), ''))), 0)
|
||||
FROM dbo.prItemBarcode WITH (UPDLOCK, HOLDLOCK)
|
||||
`).Scan(&maxBarcode)
|
||||
return maxBarcode, err
|
||||
}
|
||||
|
||||
func ValidateProductionBarcodePlan(q sqlQueryRower, variants []models.OrderProductionMissingVariant, barcodeTypeCode string) ([]models.OrderProductionBarcodeValidation, error) {
|
||||
typeCode := strings.ToUpper(strings.TrimSpace(barcodeTypeCode))
|
||||
if len(variants) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
validations := make([]models.OrderProductionBarcodeValidation, 0)
|
||||
typeExists, err := barcodeTypeExists(q, typeCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !typeExists {
|
||||
validations = append(validations, models.OrderProductionBarcodeValidation{
|
||||
Code: "invalid_barcode_type",
|
||||
Message: fmt.Sprintf("Barkod tipi bulunamadi: %s", typeCode),
|
||||
BarcodeTypeCode: typeCode,
|
||||
})
|
||||
return validations, nil
|
||||
}
|
||||
|
||||
sorted := append([]models.OrderProductionMissingVariant(nil), variants...)
|
||||
sort.Slice(sorted, func(i, j int) bool {
|
||||
left := sorted[i]
|
||||
right := sorted[j]
|
||||
leftKey := fmt.Sprintf("%05d|%s|%s|%s|%s|%s", left.ItemTypeCode, left.ItemCode, left.ColorCode, left.ItemDim1Code, left.ItemDim2Code, left.ItemDim3Code)
|
||||
rightKey := fmt.Sprintf("%05d|%s|%s|%s|%s|%s", right.ItemTypeCode, right.ItemCode, right.ColorCode, right.ItemDim1Code, right.ItemDim2Code, right.ItemDim3Code)
|
||||
return leftKey < rightKey
|
||||
})
|
||||
|
||||
maxBarcode, err := maxNumericBarcode(q)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nextOffset := int64(0)
|
||||
planned := make(map[string]struct{}, len(sorted))
|
||||
for _, variant := range sorted {
|
||||
existingBarcode, exists, err := existingVariantBarcode(q, typeCode, variant.ItemTypeCode, variant.ItemCode, variant.ColorCode, variant.ItemDim1Code, variant.ItemDim2Code, variant.ItemDim3Code)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if exists && existingBarcode != "" {
|
||||
continue
|
||||
}
|
||||
|
||||
nextOffset++
|
||||
barcode := strconv.FormatInt(maxBarcode+nextOffset, 10)
|
||||
if _, duplicated := planned[barcode]; duplicated {
|
||||
validations = append(validations, models.OrderProductionBarcodeValidation{
|
||||
Code: "barcode_duplicate_in_plan",
|
||||
Message: fmt.Sprintf("Planlanan barkod ayni istekte birden fazla kez olusuyor: %s", barcode),
|
||||
Barcode: barcode,
|
||||
BarcodeTypeCode: typeCode,
|
||||
ItemTypeCode: variant.ItemTypeCode,
|
||||
ItemCode: strings.TrimSpace(variant.ItemCode),
|
||||
ColorCode: strings.TrimSpace(variant.ColorCode),
|
||||
ItemDim1Code: strings.TrimSpace(variant.ItemDim1Code),
|
||||
ItemDim2Code: strings.TrimSpace(variant.ItemDim2Code),
|
||||
ItemDim3Code: strings.TrimSpace(variant.ItemDim3Code),
|
||||
})
|
||||
continue
|
||||
}
|
||||
planned[barcode] = struct{}{}
|
||||
|
||||
inUse, err := barcodeExists(q, barcode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if inUse {
|
||||
validations = append(validations, models.OrderProductionBarcodeValidation{
|
||||
Code: "barcode_in_use",
|
||||
Message: fmt.Sprintf("Barkod daha once kullanilmis: %s (%s / %s / %s / %s)", barcode, strings.TrimSpace(variant.ItemCode), strings.TrimSpace(variant.ColorCode), strings.TrimSpace(variant.ItemDim1Code), strings.TrimSpace(variant.ItemDim2Code)),
|
||||
Barcode: barcode,
|
||||
BarcodeTypeCode: typeCode,
|
||||
ItemTypeCode: variant.ItemTypeCode,
|
||||
ItemCode: strings.TrimSpace(variant.ItemCode),
|
||||
ColorCode: strings.TrimSpace(variant.ColorCode),
|
||||
ItemDim1Code: strings.TrimSpace(variant.ItemDim1Code),
|
||||
ItemDim2Code: strings.TrimSpace(variant.ItemDim2Code),
|
||||
ItemDim3Code: strings.TrimSpace(variant.ItemDim3Code),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return validations, nil
|
||||
}
|
||||
|
||||
func UpsertItemBarcodesTx(tx *sql.Tx, orderHeaderID string, lines []models.OrderProductionUpdateLine, username string) (int64, error) {
|
||||
start := time.Now()
|
||||
if len(lines) == 0 {
|
||||
log.Printf("[UpsertItemBarcodesTx] lines=0 inserted=0 duration_ms=0")
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
lineIDs := make([]string, 0, len(lines))
|
||||
seen := make(map[string]struct{}, len(lines))
|
||||
for _, line := range lines {
|
||||
lineID := strings.TrimSpace(line.OrderLineID)
|
||||
if lineID == "" {
|
||||
continue
|
||||
}
|
||||
if _, ok := seen[lineID]; ok {
|
||||
continue
|
||||
}
|
||||
seen[lineID] = struct{}{}
|
||||
lineIDs = append(lineIDs, lineID)
|
||||
}
|
||||
if len(lineIDs) == 0 {
|
||||
log.Printf("[UpsertItemBarcodesTx] lines=%d uniqueLineIDs=0 inserted=0 duration_ms=%d", len(lines), time.Since(start).Milliseconds())
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
const chunkSize = 900
|
||||
var inserted int64
|
||||
|
||||
for i := 0; i < len(lineIDs); i += chunkSize {
|
||||
end := i + chunkSize
|
||||
if end > len(lineIDs) {
|
||||
end = len(lineIDs)
|
||||
}
|
||||
chunk := lineIDs[i:end]
|
||||
|
||||
values := make([]string, 0, len(chunk))
|
||||
args := make([]any, 0, len(chunk)+2)
|
||||
paramPos := 1
|
||||
for _, lineID := range chunk {
|
||||
values = append(values, fmt.Sprintf("(@p%d)", paramPos))
|
||||
args = append(args, lineID)
|
||||
paramPos++
|
||||
}
|
||||
|
||||
orderHeaderParam := paramPos
|
||||
usernameParam := paramPos + 1
|
||||
args = append(args, orderHeaderID, username)
|
||||
|
||||
query := fmt.Sprintf(`
|
||||
SET NOCOUNT ON;
|
||||
WITH srcLine (OrderLineID) AS (
|
||||
SELECT *
|
||||
FROM (VALUES %s) AS v (OrderLineID)
|
||||
),
|
||||
src AS (
|
||||
SELECT DISTINCT
|
||||
l.ItemTypeCode,
|
||||
UPPER(LTRIM(RTRIM(ISNULL(l.ItemCode, '')))) AS ItemCode,
|
||||
UPPER(LTRIM(RTRIM(ISNULL(l.ColorCode, '')))) AS ColorCode,
|
||||
UPPER(LTRIM(RTRIM(ISNULL(l.ItemDim1Code, '')))) AS ItemDim1Code,
|
||||
UPPER(LTRIM(RTRIM(ISNULL(l.ItemDim2Code, '')))) AS ItemDim2Code,
|
||||
CAST('' AS NVARCHAR(50)) AS ItemDim3Code
|
||||
FROM dbo.trOrderLine l WITH (UPDLOCK, HOLDLOCK)
|
||||
JOIN srcLine s
|
||||
ON CAST(l.OrderLineID AS NVARCHAR(50)) = s.OrderLineID
|
||||
WHERE l.OrderHeaderID = @p%d
|
||||
AND NULLIF(LTRIM(RTRIM(ISNULL(l.ItemCode, ''))), '') IS NOT NULL
|
||||
),
|
||||
missing AS (
|
||||
SELECT
|
||||
s.ItemTypeCode,
|
||||
s.ItemCode,
|
||||
s.ColorCode,
|
||||
s.ItemDim1Code,
|
||||
s.ItemDim2Code,
|
||||
s.ItemDim3Code,
|
||||
ROW_NUMBER() OVER (
|
||||
ORDER BY s.ItemCode, s.ColorCode, s.ItemDim1Code, s.ItemDim2Code, s.ItemDim3Code
|
||||
) AS RowNo
|
||||
FROM src s
|
||||
LEFT JOIN dbo.prItemBarcode b WITH (UPDLOCK, HOLDLOCK)
|
||||
ON UPPER(LTRIM(RTRIM(ISNULL(b.BarcodeTypeCode, '')))) = 'BAGGI3'
|
||||
AND b.ItemTypeCode = s.ItemTypeCode
|
||||
AND UPPER(LTRIM(RTRIM(ISNULL(b.ItemCode, '')))) = s.ItemCode
|
||||
AND UPPER(LTRIM(RTRIM(ISNULL(b.ColorCode, '')))) = s.ColorCode
|
||||
AND UPPER(LTRIM(RTRIM(ISNULL(b.ItemDim1Code, '')))) = s.ItemDim1Code
|
||||
AND UPPER(LTRIM(RTRIM(ISNULL(b.ItemDim2Code, '')))) = s.ItemDim2Code
|
||||
AND UPPER(LTRIM(RTRIM(ISNULL(b.ItemDim3Code, '')))) = s.ItemDim3Code
|
||||
AND UPPER(LTRIM(RTRIM(ISNULL(b.UnitOfMeasureCode, '')))) = 'AD'
|
||||
WHERE b.Barcode IS NULL
|
||||
)
|
||||
INSERT INTO dbo.prItemBarcode (
|
||||
Barcode,
|
||||
BarcodeTypeCode,
|
||||
ItemTypeCode,
|
||||
ItemCode,
|
||||
ColorCode,
|
||||
ItemDim1Code,
|
||||
ItemDim2Code,
|
||||
ItemDim3Code,
|
||||
UnitOfMeasureCode,
|
||||
Qty,
|
||||
CreatedUserName,
|
||||
CreatedDate,
|
||||
LastUpdatedUserName,
|
||||
LastUpdatedDate,
|
||||
RowGuid
|
||||
)
|
||||
SELECT
|
||||
CAST(seed.MaxBarcode + m.RowNo AS NVARCHAR(50)) AS Barcode,
|
||||
'BAGGI3',
|
||||
m.ItemTypeCode,
|
||||
m.ItemCode,
|
||||
m.ColorCode,
|
||||
m.ItemDim1Code,
|
||||
m.ItemDim2Code,
|
||||
m.ItemDim3Code,
|
||||
'AD',
|
||||
1,
|
||||
@p%d,
|
||||
GETDATE(),
|
||||
@p%d,
|
||||
GETDATE(),
|
||||
NEWID()
|
||||
FROM missing m
|
||||
CROSS JOIN (
|
||||
SELECT ISNULL(MAX(TRY_CONVERT(BIGINT, NULLIF(LTRIM(RTRIM(Barcode)), ''))), 0) AS MaxBarcode
|
||||
FROM dbo.prItemBarcode WITH (UPDLOCK, HOLDLOCK)
|
||||
) seed;
|
||||
|
||||
SELECT @@ROWCOUNT AS Inserted;
|
||||
`, strings.Join(values, ","), orderHeaderParam, usernameParam, usernameParam)
|
||||
|
||||
chunkStart := time.Now()
|
||||
var chunkInserted int64
|
||||
if err := tx.QueryRow(query, args...).Scan(&chunkInserted); err != nil {
|
||||
return inserted, fmt.Errorf("upsert item barcodes chunk failed chunkStart=%d chunkEnd=%d duration_ms=%d: %w", i, end, time.Since(chunkStart).Milliseconds(), err)
|
||||
}
|
||||
inserted += chunkInserted
|
||||
log.Printf("[UpsertItemBarcodesTx] orderHeaderID=%s chunk=%d-%d chunkInserted=%d cumulative=%d duration_ms=%d",
|
||||
orderHeaderID, i, end, chunkInserted, inserted, time.Since(chunkStart).Milliseconds())
|
||||
}
|
||||
|
||||
log.Printf("[UpsertItemBarcodesTx] orderHeaderID=%s lines=%d uniqueLineIDs=%d inserted=%d duration_ms=%d",
|
||||
orderHeaderID, len(lines), len(lineIDs), inserted, time.Since(start).Milliseconds())
|
||||
return inserted, nil
|
||||
}
|
||||
|
||||
func UpsertItemAttributesTx(tx *sql.Tx, attrs []models.OrderProductionItemAttributeRow, username string) (int64, error) {
|
||||
start := time.Now()
|
||||
if len(attrs) == 0 {
|
||||
|
||||
Reference in New Issue
Block a user