From e1064010f3c80d580e0eed4367e47856d6c890cf Mon Sep 17 00:00:00 2001 From: M_Kececi Date: Fri, 3 Apr 2026 14:16:05 +0300 Subject: [PATCH] Merge remote-tracking branch 'origin/master' --- svc/main.go | 5 +++ svc/models/productattributes.go | 6 +++ svc/queries/productattributes.go | 10 +++++ svc/routes/productattributes.go | 47 +++++++++++++++++++ ui/src/pages/OrderProductionUpdate.vue | 55 +++++++++++++++++++---- ui/src/stores/OrderProductionItemStore.js | 19 ++++++++ 6 files changed, 133 insertions(+), 9 deletions(-) diff --git a/svc/main.go b/svc/main.go index 37d5576..2e7771a 100644 --- a/svc/main.go +++ b/svc/main.go @@ -603,6 +603,11 @@ func InitRoutes(pgDB *sql.DB, mssql *sql.DB, ml *mailer.GraphMailer) *mux.Router "order", "view", wrapV3(http.HandlerFunc(routes.GetProductAttributesHandler)), ) + bindV3(r, pgDB, + "/api/product-item-attributes", "GET", + "order", "view", + wrapV3(http.HandlerFunc(routes.GetProductItemAttributesHandler)), + ) bindV3(r, pgDB, "/api/product-stock-query", "GET", "order", "view", diff --git a/svc/models/productattributes.go b/svc/models/productattributes.go index 77b6c46..d008b24 100644 --- a/svc/models/productattributes.go +++ b/svc/models/productattributes.go @@ -7,3 +7,9 @@ type ProductAttributeOption struct { AttributeCode string `json:"attribute_code"` AttributeDescription string `json:"attribute_description"` } + +type ProductItemAttributeValue struct { + ItemTypeCode int16 `json:"item_type_code"` + AttributeTypeCode int `json:"attribute_type_code"` + AttributeCode string `json:"attribute_code"` +} diff --git a/svc/queries/productattributes.go b/svc/queries/productattributes.go index 78b8f27..38d82b4 100644 --- a/svc/queries/productattributes.go +++ b/svc/queries/productattributes.go @@ -47,3 +47,13 @@ ORDER BY CASE WHEN a.AttributeCode IN ('-', '.') THEN 0 ELSE 1 END, a.AttributeCode; ` + +const GetProductItemAttributes = ` +SELECT + a.ItemTypeCode, + a.AttributeTypeCode, + ISNULL(a.AttributeCode, '') AS AttributeCode +FROM dbo.prItemAttribute AS a WITH(NOLOCK) +WHERE a.ItemTypeCode = @p1 + AND ISNULL(LTRIM(RTRIM(a.ItemCode)), '') = ISNULL(LTRIM(RTRIM(@p2)), '') +` diff --git a/svc/routes/productattributes.go b/svc/routes/productattributes.go index de3eeb8..6205186 100644 --- a/svc/routes/productattributes.go +++ b/svc/routes/productattributes.go @@ -52,3 +52,50 @@ func GetProductAttributesHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json; charset=utf-8") _ = json.NewEncoder(w).Encode(list) } + +func GetProductItemAttributesHandler(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) + } + + itemCode := r.URL.Query().Get("itemCode") + if itemCode == "" { + http.Error(w, "itemCode zorunlu", http.StatusBadRequest) + return + } + + rows, err := db.MssqlDB.Query(queries.GetProductItemAttributes, itemTypeCode, itemCode) + if err != nil { + http.Error(w, "Product item attributes alinamadi: "+err.Error(), http.StatusInternalServerError) + return + } + defer rows.Close() + + list := make([]models.ProductItemAttributeValue, 0, 64) + for rows.Next() { + var x models.ProductItemAttributeValue + if err := rows.Scan( + &x.ItemTypeCode, + &x.AttributeTypeCode, + &x.AttributeCode, + ); err != nil { + continue + } + list = append(list, x) + } + + w.Header().Set("Content-Type", "application/json; charset=utf-8") + _ = json.NewEncoder(w).Encode(list) +} diff --git a/ui/src/pages/OrderProductionUpdate.vue b/ui/src/pages/OrderProductionUpdate.vue index 508bcd1..ab2f016 100644 --- a/ui/src/pages/OrderProductionUpdate.vue +++ b/ui/src/pages/OrderProductionUpdate.vue @@ -164,7 +164,7 @@ @click="openCdItemDialog(props.row.NewItemCode)" /> [ + Number(x?.attribute_type_code || x?.AttributeTypeCode || 0), + String(x?.attribute_code || x?.AttributeCode || '').trim() + ]).filter(x => x[0] > 0) + ) + + const baseRows = fromLookup.map(row => ({ + ...row, + AttributeCode: dbMap.get(Number(row.AttributeTypeCodeNumber || 0)) || '' + })) + + attributeRows.value = Array.isArray(existingDraft) && existingDraft.length + ? JSON.parse(JSON.stringify(existingDraft)) + : JSON.parse(JSON.stringify(baseRows)) for (const row of (attributeRows.value || [])) { if (!Array.isArray(row.AllOptions)) { row.AllOptions = Array.isArray(row.Options) ? [...row.Options] : [] @@ -932,6 +950,9 @@ async function openAttributeDialog (itemCode) { row.Options = [...row.AllOptions] } } + if ((!existingDraft || !existingDraft.length) && baseRows.length) { + store.setProductAttributeDraft(code, JSON.parse(JSON.stringify(baseRows))) + } attributeDialogOpen.value = true } @@ -950,7 +971,7 @@ function saveAttributeDraft () { $q.notify({ type: 'positive', message: 'Urun ozellikleri taslagi kaydedildi.' }) } -function collectProductAttributesFromSelectedRows (selectedRows) { +async function collectProductAttributesFromSelectedRows (selectedRows) { const codeSet = [...new Set( (selectedRows || []) .map(r => String(r?.NewItemCode || '').trim().toUpperCase()) @@ -959,7 +980,23 @@ function collectProductAttributesFromSelectedRows (selectedRows) { const out = [] for (const code of codeSet) { - const rows = store.getProductAttributeDraft(code) + let rows = store.getProductAttributeDraft(code) + if (!Array.isArray(rows) || !rows.length) { + const lookup = await store.fetchProductAttributes(1) + const baseRows = buildAttributeRowsFromLookup(lookup) + const dbCurrent = await store.fetchProductItemAttributes(code, 1) + const dbMap = new Map( + (dbCurrent || []).map(x => [ + Number(x?.attribute_type_code || x?.AttributeTypeCode || 0), + String(x?.attribute_code || x?.AttributeCode || '').trim() + ]).filter(x => x[0] > 0) + ) + rows = baseRows.map(row => ({ + ...row, + AttributeCode: dbMap.get(Number(row.AttributeTypeCodeNumber || 0)) || '' + })) + store.setProductAttributeDraft(code, JSON.parse(JSON.stringify(rows))) + } if (!Array.isArray(rows) || !rows.length) { return { errMsg: `${code} icin urun ozellikleri secilmedi`, productAttributes: [] } } @@ -1185,7 +1222,7 @@ async function onBulkSubmit () { if (firstCode) openCdItemDialog(firstCode) return } - const { errMsg: attrErrMsg, productAttributes } = collectProductAttributesFromSelectedRows(selectedRows) + const { errMsg: attrErrMsg, productAttributes } = await collectProductAttributesFromSelectedRows(selectedRows) if (attrErrMsg) { $q.notify({ type: 'negative', message: attrErrMsg }) const firstCode = String(attrErrMsg.split(' ')[0] || '').trim() diff --git a/ui/src/stores/OrderProductionItemStore.js b/ui/src/stores/OrderProductionItemStore.js index 59b7ab0..261db41 100644 --- a/ui/src/stores/OrderProductionItemStore.js +++ b/ui/src/stores/OrderProductionItemStore.js @@ -50,6 +50,7 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', { secondColorRequestsByKey: {}, newSecondColorRequestsByKey: {}, productAttributesByItemType: {}, + productItemAttributesByKey: {}, cdItemLookups: null, cdItemDraftsByCode: {}, productAttributeDraftsByCode: {}, @@ -272,6 +273,24 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', { return [] } }, + async fetchProductItemAttributes (itemCode, itemTypeCode = 1, force = false) { + const code = String(itemCode || '').trim().toUpperCase() + const itc = Number(itemTypeCode || 1) + if (!code) return [] + const key = `${itc}|${code}` + if (!force && this.productItemAttributesByKey[key]) { + return this.productItemAttributesByKey[key] + } + try { + const res = await api.get('/product-item-attributes', { params: { itemTypeCode: itc, itemCode: code } }) + const list = Array.isArray(res?.data) ? res.data : [] + this.productItemAttributesByKey[key] = list + return list + } catch (err) { + this.error = err?.response?.data || err?.message || 'Urunun mevcut ozellikleri alinamadi' + return [] + } + }, async fetchCdItemLookups (force = false) { if (this.cdItemLookups && !force) return this.cdItemLookups try {