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

@@ -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
}