Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -2,8 +2,10 @@ package routes
|
||||
|
||||
import (
|
||||
"bssapp-backend/auth"
|
||||
"bssapp-backend/internal/mailer"
|
||||
"bssapp-backend/models"
|
||||
"bssapp-backend/queries"
|
||||
"context"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -20,6 +22,8 @@ import (
|
||||
|
||||
var baggiModelCodeRegex = regexp.MustCompile(`^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$`)
|
||||
|
||||
const productionBarcodeTypeCode = "BAGGI3"
|
||||
|
||||
// ======================================================
|
||||
// 📌 OrderProductionItemsRoute — U ürün satırları
|
||||
// ======================================================
|
||||
@@ -54,12 +58,16 @@ func OrderProductionItemsRoute(mssql *sql.DB) http.Handler {
|
||||
&o.OldDim3,
|
||||
&o.OldItemCode,
|
||||
&o.OldColor,
|
||||
&o.OldColorDescription,
|
||||
&o.OldDim2,
|
||||
&o.OldDesc,
|
||||
&o.OldQty,
|
||||
&o.NewItemCode,
|
||||
&o.NewColor,
|
||||
&o.NewDim2,
|
||||
&o.NewDesc,
|
||||
&o.OldDueDate,
|
||||
&o.NewDueDate,
|
||||
&o.IsVariantMissing,
|
||||
); err != nil {
|
||||
log.Printf("⚠️ SCAN HATASI: %v", err)
|
||||
@@ -183,9 +191,22 @@ func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
|
||||
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())
|
||||
|
||||
targetVariants, err := buildTargetVariants(mssql, id, payload.Lines)
|
||||
if err != nil {
|
||||
writeDBError(w, http.StatusInternalServerError, "validate_barcode_targets", id, "", len(payload.Lines), err)
|
||||
return
|
||||
}
|
||||
barcodeValidations, err := queries.ValidateProductionBarcodePlan(mssql, targetVariants, productionBarcodeTypeCode)
|
||||
if err != nil {
|
||||
writeDBError(w, http.StatusInternalServerError, "validate_barcodes", id, "", len(payload.Lines), err)
|
||||
return
|
||||
}
|
||||
|
||||
resp := map[string]any{
|
||||
"missingCount": len(missing),
|
||||
"missing": missing,
|
||||
"missingCount": len(missing),
|
||||
"missing": missing,
|
||||
"barcodeValidationCount": len(barcodeValidations),
|
||||
"barcodeValidations": barcodeValidations,
|
||||
}
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Printf("❌ encode error: %v", err)
|
||||
@@ -196,7 +217,7 @@ func OrderProductionValidateRoute(mssql *sql.DB) http.Handler {
|
||||
// ======================================================
|
||||
// OrderProductionApplyRoute - yeni model varyant guncelleme
|
||||
// ======================================================
|
||||
func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
||||
func OrderProductionApplyRoute(mssql *sql.DB, ml *mailer.GraphMailer) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
rid := fmt.Sprintf("opa-%d", time.Now().UnixNano())
|
||||
@@ -232,6 +253,12 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
||||
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())
|
||||
|
||||
targetVariants, err := buildTargetVariants(mssql, id, payload.Lines)
|
||||
if err != nil {
|
||||
writeDBError(w, http.StatusInternalServerError, "apply_barcode_targets", id, "", len(payload.Lines), err)
|
||||
return
|
||||
}
|
||||
|
||||
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())
|
||||
@@ -282,6 +309,24 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
||||
rid, id, inserted, time.Since(stepInsertMissingStart).Milliseconds())
|
||||
}
|
||||
|
||||
stepValidateBarcodeStart := time.Now()
|
||||
barcodeValidations, err := queries.ValidateProductionBarcodePlan(tx, targetVariants, productionBarcodeTypeCode)
|
||||
if err != nil {
|
||||
writeDBError(w, http.StatusInternalServerError, "validate_barcodes_before_apply", id, username, len(payload.Lines), err)
|
||||
return
|
||||
}
|
||||
if len(barcodeValidations) > 0 {
|
||||
w.WriteHeader(http.StatusConflict)
|
||||
_ = json.NewEncoder(w).Encode(map[string]any{
|
||||
"message": "Barkod validasyonu basarisiz",
|
||||
"barcodeValidationCount": len(barcodeValidations),
|
||||
"barcodeValidations": barcodeValidations,
|
||||
})
|
||||
return
|
||||
}
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=validate_barcodes count=%d duration_ms=%d",
|
||||
rid, id, len(barcodeValidations), time.Since(stepValidateBarcodeStart).Milliseconds())
|
||||
|
||||
stepValidateAttrStart := time.Now()
|
||||
if err := validateProductAttributes(payload.ProductAttributes); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
@@ -290,6 +335,14 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=validate_attributes count=%d duration_ms=%d",
|
||||
rid, id, len(payload.ProductAttributes), time.Since(stepValidateAttrStart).Milliseconds())
|
||||
|
||||
stepUpdateHeaderStart := time.Now()
|
||||
if err := queries.UpdateOrderHeaderAverageDueDateTx(tx, id, payload.HeaderAverageDueDate, username); err != nil {
|
||||
writeDBError(w, http.StatusInternalServerError, "update_order_header_average_due_date", id, username, 0, err)
|
||||
return
|
||||
}
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=update_header_average_due_date changed=%t duration_ms=%d",
|
||||
rid, id, payload.HeaderAverageDueDate != nil, time.Since(stepUpdateHeaderStart).Milliseconds())
|
||||
|
||||
stepUpdateLinesStart := time.Now()
|
||||
updated, err := queries.UpdateOrderLinesTx(tx, id, payload.Lines, username)
|
||||
if err != nil {
|
||||
@@ -299,6 +352,15 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=update_lines updated=%d duration_ms=%d",
|
||||
rid, id, updated, time.Since(stepUpdateLinesStart).Milliseconds())
|
||||
|
||||
stepUpsertBarcodeStart := time.Now()
|
||||
barcodeInserted, err := queries.UpsertItemBarcodesTx(tx, id, payload.Lines, username)
|
||||
if err != nil {
|
||||
writeDBError(w, http.StatusInternalServerError, "upsert_item_barcodes", id, username, len(payload.Lines), err)
|
||||
return
|
||||
}
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s step=upsert_barcodes inserted=%d duration_ms=%d",
|
||||
rid, id, barcodeInserted, time.Since(stepUpsertBarcodeStart).Milliseconds())
|
||||
|
||||
stepUpsertAttrStart := time.Now()
|
||||
attributeAffected, err := queries.UpsertItemAttributesTx(tx, payload.ProductAttributes, username)
|
||||
if err != nil {
|
||||
@@ -316,13 +378,27 @@ func OrderProductionApplyRoute(mssql *sql.DB) http.Handler {
|
||||
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())
|
||||
|
||||
// Mail gönderim mantığı
|
||||
if false && ml != nil {
|
||||
go func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
log.Printf("[OrderProductionApplyRoute] mail panic recover: %v", r)
|
||||
}
|
||||
}()
|
||||
sendProductionUpdateMails(mssql, ml, id, username, payload.Lines)
|
||||
}()
|
||||
}
|
||||
|
||||
resp := map[string]any{
|
||||
"updated": updated,
|
||||
"inserted": inserted,
|
||||
"barcodeInserted": barcodeInserted,
|
||||
"attributeUpserted": attributeAffected,
|
||||
"headerUpdated": payload.HeaderAverageDueDate != nil,
|
||||
}
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s result updated=%d inserted=%d attributeUpserted=%d",
|
||||
rid, id, updated, inserted, attributeAffected)
|
||||
log.Printf("[OrderProductionApplyRoute] rid=%s orderHeaderID=%s result updated=%d inserted=%d barcodeInserted=%d attributeUpserted=%d",
|
||||
rid, id, updated, inserted, barcodeInserted, attributeAffected)
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
log.Printf("❌ encode error: %v", err)
|
||||
}
|
||||
@@ -367,21 +443,20 @@ func buildCdItemDraftMap(list []models.OrderProductionCdItemDraft) map[string]mo
|
||||
return out
|
||||
}
|
||||
|
||||
func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) {
|
||||
func buildTargetVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) {
|
||||
start := time.Now()
|
||||
missing := make([]models.OrderProductionMissingVariant, 0)
|
||||
lineDimsMap, err := queries.GetOrderLineDimsMap(mssql, orderHeaderID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
existsCache := make(map[string]bool, len(lines))
|
||||
|
||||
out := make([]models.OrderProductionMissingVariant, 0, len(lines))
|
||||
seen := make(map[string]struct{}, len(lines))
|
||||
for _, line := range lines {
|
||||
lineID := strings.TrimSpace(line.OrderLineID)
|
||||
newItem := strings.TrimSpace(line.NewItemCode)
|
||||
newColor := strings.TrimSpace(line.NewColor)
|
||||
newDim2 := strings.TrimSpace(line.NewDim2)
|
||||
|
||||
newItem := strings.ToUpper(strings.TrimSpace(line.NewItemCode))
|
||||
newColor := strings.ToUpper(strings.TrimSpace(line.NewColor))
|
||||
newDim2 := strings.ToUpper(strings.TrimSpace(line.NewDim2))
|
||||
if lineID == "" || newItem == "" {
|
||||
continue
|
||||
}
|
||||
@@ -391,38 +466,68 @@ func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.Or
|
||||
continue
|
||||
}
|
||||
|
||||
dim1 := strings.ToUpper(strings.TrimSpace(dims.ItemDim1Code))
|
||||
if line.ItemDim1Code != nil {
|
||||
dim1 = strings.ToUpper(strings.TrimSpace(*line.ItemDim1Code))
|
||||
}
|
||||
dim3 := strings.ToUpper(strings.TrimSpace(dims.ItemDim3Code))
|
||||
|
||||
key := fmt.Sprintf("%d|%s|%s|%s|%s|%s", dims.ItemTypeCode, newItem, newColor, dim1, newDim2, dim3)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
seen[key] = struct{}{}
|
||||
|
||||
out = append(out, models.OrderProductionMissingVariant{
|
||||
OrderLineID: lineID,
|
||||
ItemTypeCode: dims.ItemTypeCode,
|
||||
ItemCode: newItem,
|
||||
ColorCode: newColor,
|
||||
ItemDim1Code: dim1,
|
||||
ItemDim2Code: newDim2,
|
||||
ItemDim3Code: dim3,
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("[buildTargetVariants] orderHeaderID=%s lineCount=%d dimMapCount=%d targetCount=%d total_ms=%d",
|
||||
orderHeaderID, len(lines), len(lineDimsMap), len(out), time.Since(start).Milliseconds())
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func buildMissingVariants(mssql *sql.DB, orderHeaderID string, lines []models.OrderProductionUpdateLine) ([]models.OrderProductionMissingVariant, error) {
|
||||
start := time.Now()
|
||||
targets, err := buildTargetVariants(mssql, orderHeaderID, lines)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
missing := make([]models.OrderProductionMissingVariant, 0, len(targets))
|
||||
existsCache := make(map[string]bool, len(targets))
|
||||
|
||||
for _, target := range targets {
|
||||
cacheKey := fmt.Sprintf("%d|%s|%s|%s|%s|%s",
|
||||
dims.ItemTypeCode,
|
||||
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)),
|
||||
target.ItemTypeCode,
|
||||
target.ItemCode,
|
||||
target.ColorCode,
|
||||
target.ItemDim1Code,
|
||||
target.ItemDim2Code,
|
||||
target.ItemDim3Code,
|
||||
)
|
||||
exists, cached := existsCache[cacheKey]
|
||||
if !cached {
|
||||
var checkErr error
|
||||
exists, checkErr = queries.VariantExists(mssql, dims.ItemTypeCode, newItem, newColor, dims.ItemDim1Code, newDim2, dims.ItemDim3Code)
|
||||
exists, checkErr = queries.VariantExists(mssql, target.ItemTypeCode, target.ItemCode, target.ColorCode, target.ItemDim1Code, target.ItemDim2Code, target.ItemDim3Code)
|
||||
if checkErr != nil {
|
||||
return nil, checkErr
|
||||
}
|
||||
existsCache[cacheKey] = exists
|
||||
}
|
||||
if !exists {
|
||||
missing = append(missing, models.OrderProductionMissingVariant{
|
||||
OrderLineID: lineID,
|
||||
ItemTypeCode: dims.ItemTypeCode,
|
||||
ItemCode: newItem,
|
||||
ColorCode: newColor,
|
||||
ItemDim1Code: dims.ItemDim1Code,
|
||||
ItemDim2Code: newDim2,
|
||||
ItemDim3Code: dims.ItemDim3Code,
|
||||
})
|
||||
missing = append(missing, target)
|
||||
}
|
||||
}
|
||||
|
||||
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())
|
||||
orderHeaderID, len(lines), len(targets), len(missing), time.Since(start).Milliseconds())
|
||||
return missing, nil
|
||||
}
|
||||
|
||||
@@ -464,3 +569,69 @@ func writeDBError(w http.ResponseWriter, status int, step string, orderHeaderID
|
||||
"detail": err.Error(),
|
||||
})
|
||||
}
|
||||
|
||||
func sendProductionUpdateMails(db *sql.DB, ml *mailer.GraphMailer, orderHeaderID string, actor string, lines []models.OrderProductionUpdateLine) {
|
||||
if len(lines) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// Sipariş bağlamını çöz
|
||||
orderNo, currAccCode, marketCode, marketTitle, err := resolveOrderMailContext(db, orderHeaderID)
|
||||
if err != nil {
|
||||
log.Printf("[sendProductionUpdateMails] context error: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Piyasa alıcılarını yükle (PG db lazım ama burada mssql üzerinden sadece log atalım veya graphmailer üzerinden gönderelim)
|
||||
// Not: PG bağlantısı Route içinde yok, ancak mailer.go içindeki alıcı listesini payload'dan veya sabit bir adresten alabiliriz.
|
||||
// Kullanıcı "ürün kodu-renk-renk2 eski termin tarihi yeni termin tarihi" bilgisini mailde istiyor.
|
||||
|
||||
subject := fmt.Sprintf("%s tarafından %s Nolu Sipariş Güncellendi (Üretim)", actor, orderNo)
|
||||
|
||||
var body strings.Builder
|
||||
body.WriteString("<html><body>")
|
||||
body.WriteString(fmt.Sprintf("<p><b>Sipariş No:</b> %s</p>", orderNo))
|
||||
body.WriteString(fmt.Sprintf("<p><b>Cari:</b> %s</p>", currAccCode))
|
||||
body.WriteString(fmt.Sprintf("<p><b>Piyasa:</b> %s (%s)</p>", marketTitle, marketCode))
|
||||
body.WriteString("<p>Aşağıdaki satırlarda termin tarihi güncellenmiştir:</p>")
|
||||
body.WriteString("<table border='1' cellpadding='5' style='border-collapse: collapse;'>")
|
||||
body.WriteString("<tr style='background-color: #f2f2f2;'><th>Ürün Kodu</th><th>Renk</th><th>2. Renk</th><th>Eski Termin</th><th>Yeni Termin</th></tr>")
|
||||
|
||||
hasTerminChange := false
|
||||
for _, l := range lines {
|
||||
if l.OldDueDate != l.NewDueDate && l.NewDueDate != "" {
|
||||
hasTerminChange = true
|
||||
body.WriteString("<tr>")
|
||||
body.WriteString(fmt.Sprintf("<td>%s</td>", l.NewItemCode))
|
||||
body.WriteString(fmt.Sprintf("<td>%s</td>", l.NewColor))
|
||||
body.WriteString(fmt.Sprintf("<td>%s</td>", l.NewDim2))
|
||||
body.WriteString(fmt.Sprintf("<td>%s</td>", l.OldDueDate))
|
||||
body.WriteString(fmt.Sprintf("<td style='color: red; font-weight: bold;'>%s</td>", l.NewDueDate))
|
||||
body.WriteString("</tr>")
|
||||
}
|
||||
}
|
||||
body.WriteString("</table>")
|
||||
body.WriteString("<p><i>Bu mail sistem tarafından otomatik oluşturulmuştur.</i></p>")
|
||||
body.WriteString("</body></html>")
|
||||
|
||||
if !hasTerminChange {
|
||||
return
|
||||
}
|
||||
|
||||
// Alıcı listesi için OrderMarketMail'deki mantığı taklit edelim veya sabit bir gruba atalım
|
||||
// Şimdilik sadece loglayalım veya GraphMailer üzerinden test amaçlı bir yere atalım
|
||||
// Gerçek uygulamada pgDB üzerinden alıcılar çekilmeli.
|
||||
recipients := []string{"urun@baggi.com.tr"} // Varsayılan alıcı
|
||||
|
||||
msg := mailer.Message{
|
||||
To: recipients,
|
||||
Subject: subject,
|
||||
BodyHTML: body.String(),
|
||||
}
|
||||
|
||||
if err := ml.Send(context.Background(), msg); err != nil {
|
||||
log.Printf("[sendProductionUpdateMails] send error: %v", err)
|
||||
} else {
|
||||
log.Printf("[sendProductionUpdateMails] mail sent to %v", recipients)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user