diff --git a/svc/routes/order_mail.go b/svc/routes/order_mail.go index a6e4793..20cf7c6 100644 --- a/svc/routes/order_mail.go +++ b/svc/routes/order_mail.go @@ -9,11 +9,16 @@ import ( "net/http" "strings" + "bssapp-backend/auth" "bssapp-backend/internal/mailer" ) type sendOrderMarketMailPayload struct { - OrderHeaderID string `json:"orderHeaderID"` + OrderHeaderID string `json:"orderHeaderID"` + Operation string `json:"operation"` + DeletedItems []string `json:"deletedItems"` + UpdatedItems []string `json:"updatedItems"` + AddedItems []string `json:"addedItems"` } func SendOrderMarketMailHandler(pg *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) http.HandlerFunc { @@ -28,6 +33,11 @@ func SendOrderMarketMailHandler(pg *sql.DB, mssql *sql.DB, ml *mailer.GraphMaile http.Error(w, "database not initialized", http.StatusInternalServerError) return } + claims, ok := auth.GetClaimsFromContext(r.Context()) + if !ok || claims == nil { + http.Error(w, "unauthorized", http.StatusUnauthorized) + return + } var payload sendOrderMarketMailPayload if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { @@ -80,14 +90,52 @@ func SendOrderMarketMailHandler(pg *sql.DB, mssql *sql.DB, ml *mailer.GraphMaile marketLabel = strings.TrimSpace(marketCode) } - subject := fmt.Sprintf("Sipariş %s - %s", number, marketLabel) - bodyHTML := fmt.Sprintf( - `

Sipariş kaydı oluşturuldu/güncellendi.

Sipariş No: %s
Cari: %s
Piyasa: %s

PDF ektedir.

`, - htmlEsc(number), - htmlEsc(currAccCode), - htmlEsc(marketLabel), + actor := strings.TrimSpace(claims.Username) + if actor == "" { + actor = strings.TrimSpace(claims.V3Username) + } + if actor == "" { + actor = "Bilinmeyen Kullanici" + } + + op := strings.ToLower(strings.TrimSpace(payload.Operation)) + isUpdate := op == "update" + + subjectAction := "SİPARİŞ KAYDI OLUŞTURULDU" + if isUpdate { + subjectAction = "SİPARİŞ GÜNCELLENDİ." + } + subject := fmt.Sprintf("%s kullanıcısı tarafından %s %s", actor, number, subjectAction) + + cariDetail := "" + customerRep := "" + if header != nil { + cariDetail = strings.TrimSpace(header.CurrAccName) + customerRep = strings.TrimSpace(header.CustomerRep) + } + + body := make([]string, 0, 12) + body = append(body, + `

`, + fmt.Sprintf(`Cari Kodu: %s
`, htmlEsc(currAccCode)), + fmt.Sprintf(`Cari Detay: %s
`, htmlEsc(cariDetail)), + fmt.Sprintf(`Müşteri Temsilcisi: %s
`, htmlEsc(customerRep)), + fmt.Sprintf(`Piyasa: %s`, htmlEsc(marketLabel)), + `

`, ) + if isUpdate { + body = append(body, + renderItemListHTML("Silinen Ürün Kodları", payload.DeletedItems), + renderItemListHTML("Güncellenen Ürün Kodları", payload.UpdatedItems), + renderItemListHTML("Eklenen Ürün Kodları", payload.AddedItems), + ) + } + + body = append(body, `

Bu sipariş BaggiSS App Uygulamasından oluşturulmuştur.

`) + body = append(body, `

PDF ektedir.

`) + bodyHTML := strings.Join(body, "\n") + fileNo := sanitizeFileName(number) if fileNo == "" { fileNo = orderID @@ -256,3 +304,31 @@ func htmlEsc(s string) string { ) return r.Replace(s) } + +func renderItemListHTML(title string, items []string) string { + clean := make([]string, 0, len(items)) + seen := make(map[string]struct{}, len(items)) + for _, raw := range items { + v := strings.TrimSpace(raw) + if v == "" { + continue + } + if _, ok := seen[v]; ok { + continue + } + seen[v] = struct{}{} + clean = append(clean, v) + } + + if len(clean) == 0 { + return fmt.Sprintf(`

%s: Yok

`, htmlEsc(title)) + } + + b := make([]string, 0, len(clean)+3) + b = append(b, fmt.Sprintf(`

%s:
`, htmlEsc(title))) + for _, item := range clean { + b = append(b, "- "+htmlEsc(item)+"
") + } + b = append(b, `

`) + return strings.Join(b, "\n") +} diff --git a/ui/src/stores/orderentryStore.js b/ui/src/stores/orderentryStore.js index 4c9b38b..02bd2f3 100644 --- a/ui/src/stores/orderentryStore.js +++ b/ui/src/stores/orderentryStore.js @@ -400,7 +400,68 @@ export const useOrderEntryStore = defineStore('orderentry', { } , - async sendOrderToMarketMails(orderId) { + buildMailLineLabel(line) { + if (!line || typeof line !== 'object') return '' + + const item = String(line.ItemCode || '').trim() + const color1 = String(line.ColorCode || '').trim() + const color2 = String(line.ItemDim2Code || '').trim() + const desc = String(line.LineDescription || '').trim() + + if (!item) return '' + + const colorPart = color2 ? `${color1}-${color2}` : color1 + return [item, colorPart, desc].filter(Boolean).join(' ') + } + , + + buildOrderMailPayload(lines = [], isNew = false) { + const uniq = (arr) => [...new Set((arr || []).map(v => String(v || '').trim()).filter(Boolean))] + + const normalized = Array.isArray(lines) ? lines : [] + const mapLabel = (ln) => this.buildMailLineLabel(ln) + + if (isNew) { + return { + operation: 'create', + deletedItems: [], + updatedItems: [], + addedItems: uniq( + normalized + .filter(ln => !ln?._deleteSignal) + .map(mapLabel) + ) + } + } + + const deletedItems = uniq( + normalized + .filter(ln => ln?._deleteSignal === true) + .map(mapLabel) + ) + + const updatedItems = uniq( + normalized + .filter(ln => !ln?._deleteSignal && !!ln?.OrderLineID && ln?._dirty === true) + .map(mapLabel) + ) + + const addedItems = uniq( + normalized + .filter(ln => !ln?._deleteSignal && !ln?.OrderLineID) + .map(mapLabel) + ) + + return { + operation: 'update', + deletedItems, + updatedItems, + addedItems + } + } + , + + async sendOrderToMarketMails(orderId, payload = {}) { const id = String(orderId || this.header?.OrderHeaderID || '').trim() if (!id) { throw new Error('Sipariş ID bulunamadı') @@ -408,7 +469,11 @@ export const useOrderEntryStore = defineStore('orderentry', { try { const res = await api.post('/order/send-market-mail', { - orderHeaderID: id + orderHeaderID: id, + operation: payload?.operation || 'create', + deletedItems: Array.isArray(payload?.deletedItems) ? payload.deletedItems : [], + updatedItems: Array.isArray(payload?.updatedItems) ? payload.updatedItems : [], + addedItems: Array.isArray(payload?.addedItems) ? payload.addedItems : [] }) return res?.data || {} } catch (err) { @@ -2867,7 +2932,8 @@ export const useOrderEntryStore = defineStore('orderentry', { // 📧 Piyasa eşleşen alıcılara sipariş PDF gönderimi (kayıt başarılı olduktan sonra) try { - const mailRes = await this.sendOrderToMarketMails(serverOrderId) + const mailPayload = this.buildOrderMailPayload(lines, isNew) + const mailRes = await this.sendOrderToMarketMails(serverOrderId, mailPayload) const sentCount = Number(mailRes?.sentCount || 0) $q.notify({ type: 'positive',