Merge remote-tracking branch 'origin/master'
This commit is contained in:
@@ -326,6 +326,50 @@ RETURNING id
|
|||||||
return newID, true, nil
|
return newID, true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindActivePricingParameterByScope(ctx context.Context, tx *sql.Tx, row pricingParameterRow) (int64, error) {
|
||||||
|
row.AskiliYan = strings.TrimSpace(row.AskiliYan)
|
||||||
|
row.Kategori = strings.TrimSpace(row.Kategori)
|
||||||
|
row.UrunIlkGrubu = strings.TrimSpace(row.UrunIlkGrubu)
|
||||||
|
row.UrunAnaGrubu = strings.TrimSpace(row.UrunAnaGrubu)
|
||||||
|
row.UrunAltGrubu = strings.TrimSpace(row.UrunAltGrubu)
|
||||||
|
row.Icerik = strings.TrimSpace(row.Icerik)
|
||||||
|
row.Marka = strings.TrimSpace(row.Marka)
|
||||||
|
row.BrandCode = strings.TrimSpace(row.BrandCode)
|
||||||
|
row.BrandGroupSec = strings.TrimSpace(row.BrandGroupSec)
|
||||||
|
|
||||||
|
var id int64
|
||||||
|
err := tx.QueryRowContext(ctx, `
|
||||||
|
SELECT id
|
||||||
|
FROM mk_urunpricingprmtr
|
||||||
|
WHERE askili_yan=$1
|
||||||
|
AND kategori=$2
|
||||||
|
AND urun_ilk_grubu=$3
|
||||||
|
AND urun_ana_grubu=$4
|
||||||
|
AND urun_alt_grubu=$5
|
||||||
|
AND icerik=$6
|
||||||
|
AND marka=$7
|
||||||
|
AND brand_code=$8
|
||||||
|
AND brand_group_sec=$9
|
||||||
|
AND is_active=TRUE
|
||||||
|
ORDER BY last_seen_at DESC, id DESC
|
||||||
|
LIMIT 1
|
||||||
|
`,
|
||||||
|
row.AskiliYan,
|
||||||
|
row.Kategori,
|
||||||
|
row.UrunIlkGrubu,
|
||||||
|
row.UrunAnaGrubu,
|
||||||
|
row.UrunAltGrubu,
|
||||||
|
row.Icerik,
|
||||||
|
row.Marka,
|
||||||
|
row.BrandCode,
|
||||||
|
row.BrandGroupSec,
|
||||||
|
).Scan(&id)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return id, nil
|
||||||
|
}
|
||||||
|
|
||||||
func SyncPricingParametersFromMSSQL(ctx context.Context, mssql *sql.DB, pg *sql.DB) (PricingParameterSyncResult, error) {
|
func SyncPricingParametersFromMSSQL(ctx context.Context, mssql *sql.DB, pg *sql.DB) (PricingParameterSyncResult, error) {
|
||||||
out := PricingParameterSyncResult{}
|
out := PricingParameterSyncResult{}
|
||||||
startedAt := time.Now()
|
startedAt := time.Now()
|
||||||
|
|||||||
@@ -70,6 +70,8 @@ type PricingRuleImportPayload struct {
|
|||||||
type PricingRuleImportResult struct {
|
type PricingRuleImportResult struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Processed int `json:"processed"`
|
Processed int `json:"processed"`
|
||||||
|
Matched int `json:"matched"`
|
||||||
|
Skipped int `json:"skipped"`
|
||||||
Updated int `json:"updated"`
|
Updated int `json:"updated"`
|
||||||
ActivatedScopeCount int `json:"activated_scope_count"`
|
ActivatedScopeCount int `json:"activated_scope_count"`
|
||||||
ErrorCount int `json:"error_count"`
|
ErrorCount int `json:"error_count"`
|
||||||
@@ -162,14 +164,15 @@ func ImportPricingRulesHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
defer tx.Rollback()
|
defer tx.Rollback()
|
||||||
|
|
||||||
updated := 0
|
updated := 0
|
||||||
activatedScopeCount := 0
|
matched := 0
|
||||||
|
skipped := 0
|
||||||
for _, raw := range payload.Items {
|
for _, raw := range payload.Items {
|
||||||
if raw.TryWholesaleStep < 0 || raw.TryRetailStep < 0 || raw.UsdWholesaleStep < 0 || raw.UsdRetailStep < 0 || raw.EurWholesaleStep < 0 || raw.EurRetailStep < 0 {
|
if raw.TryWholesaleStep < 0 || raw.TryRetailStep < 0 || raw.UsdWholesaleStep < 0 || raw.UsdRetailStep < 0 || raw.EurWholesaleStep < 0 || raw.EurRetailStep < 0 {
|
||||||
http.Error(w, "invalid rounding step", http.StatusBadRequest)
|
http.Error(w, "invalid rounding step", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pricingParameterID, activated, err := queries.EnsureActivePricingParameterByScope(ctx, tx, queries.PricingParameterRowForImport(
|
pricingParameterID, err := queries.FindActivePricingParameterByScope(ctx, tx, queries.PricingParameterRowForImport(
|
||||||
raw.AskiliYan,
|
raw.AskiliYan,
|
||||||
raw.Kategori,
|
raw.Kategori,
|
||||||
raw.UrunIlkGrubu,
|
raw.UrunIlkGrubu,
|
||||||
@@ -180,13 +183,15 @@ func ImportPricingRulesHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
raw.BrandCode,
|
raw.BrandCode,
|
||||||
raw.BrandGroupSec,
|
raw.BrandGroupSec,
|
||||||
))
|
))
|
||||||
|
if err == sql.ErrNoRows {
|
||||||
|
skipped++
|
||||||
|
continue
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
http.Error(w, "pricing parameter resolve error", http.StatusInternalServerError)
|
http.Error(w, "pricing parameter resolve error", http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if activated {
|
matched++
|
||||||
activatedScopeCount++
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = queries.UpsertPricingRule(ctx, tx, queries.PricingRuleSaveItem{
|
_, err = queries.UpsertPricingRule(ctx, tx, queries.PricingRuleSaveItem{
|
||||||
PricingParameterID: pricingParameterID,
|
PricingParameterID: pricingParameterID,
|
||||||
@@ -233,8 +238,10 @@ func ImportPricingRulesHandler(pg *sql.DB) http.HandlerFunc {
|
|||||||
_ = json.NewEncoder(w).Encode(PricingRuleImportResult{
|
_ = json.NewEncoder(w).Encode(PricingRuleImportResult{
|
||||||
Success: true,
|
Success: true,
|
||||||
Processed: len(payload.Items),
|
Processed: len(payload.Items),
|
||||||
|
Matched: matched,
|
||||||
|
Skipped: skipped,
|
||||||
Updated: updated,
|
Updated: updated,
|
||||||
ActivatedScopeCount: activatedScopeCount,
|
ActivatedScopeCount: 0,
|
||||||
ErrorCount: 0,
|
ErrorCount: 0,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -545,7 +552,7 @@ func buildPricingRuleCSV(rows []queries.PricingParameterRuleRow) string {
|
|||||||
row.UrunAltGrubu,
|
row.UrunAltGrubu,
|
||||||
row.Icerik,
|
row.Icerik,
|
||||||
row.Marka,
|
row.Marka,
|
||||||
row.BrandCode,
|
csvExcelTextValue(row.BrandCode),
|
||||||
row.BrandGroupSec,
|
row.BrandGroupSec,
|
||||||
fmt.Sprintf("%.2f", pricingRuleNumericValue(row, "try_wholesale_step")),
|
fmt.Sprintf("%.2f", pricingRuleNumericValue(row, "try_wholesale_step")),
|
||||||
fmt.Sprintf("%.2f", pricingRuleNumericValue(row, "try_retail_step")),
|
fmt.Sprintf("%.2f", pricingRuleNumericValue(row, "try_retail_step")),
|
||||||
@@ -595,6 +602,14 @@ func csvEscapeValue(value string) string {
|
|||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func csvExcelTextValue(value string) string {
|
||||||
|
text := strings.TrimSpace(value)
|
||||||
|
if text == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return `="` + strings.ReplaceAll(text, `"`, `""`) + `"`
|
||||||
|
}
|
||||||
|
|
||||||
func splitCSV(raw string) []string {
|
func splitCSV(raw string) []string {
|
||||||
raw = strings.TrimSpace(raw)
|
raw = strings.TrimSpace(raw)
|
||||||
if raw == "" {
|
if raw == "" {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
icon="refresh"
|
icon="refresh"
|
||||||
label="Yenile"
|
label="Yenile"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="loadRows"
|
@click="refreshRows"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -291,7 +291,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
import { computed, onActivated, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
import { Notify } from 'quasar'
|
import { Notify } from 'quasar'
|
||||||
import api, { download } from 'src/services/api'
|
import api, { download } from 'src/services/api'
|
||||||
import { usePermissionStore } from 'stores/permissionStore'
|
import { usePermissionStore } from 'stores/permissionStore'
|
||||||
@@ -809,7 +809,11 @@ function parseCsvRows (text) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function normalizeImportText (value) {
|
function normalizeImportText (value) {
|
||||||
return String(value ?? '').trim().toLocaleUpperCase('tr')
|
let text = String(value ?? '').trim()
|
||||||
|
if (text.startsWith("'")) text = text.slice(1).trim()
|
||||||
|
const formulaMatch = text.match(/^=\s*"([\s\S]*)"$/)
|
||||||
|
if (formulaMatch) text = formulaMatch[1]
|
||||||
|
return text.toLocaleUpperCase('tr')
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildImportRowKeyFromObject (row) {
|
function buildImportRowKeyFromObject (row) {
|
||||||
@@ -880,7 +884,7 @@ async function onImportFileChange (event) {
|
|||||||
const csvRow = matrix[i]
|
const csvRow = matrix[i]
|
||||||
const identity = {}
|
const identity = {}
|
||||||
for (const [field, label] of importKeyFieldLabels) {
|
for (const [field, label] of importKeyFieldLabels) {
|
||||||
identity[field] = csvRow[keyHeaderIndexes[label]] ?? ''
|
identity[field] = String(csvRow[keyHeaderIndexes[label]] ?? '').trim().replace(/^'/, '').replace(/^=\s*"([\s\S]*)"$/, '$1')
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = {
|
const item = {
|
||||||
@@ -920,7 +924,7 @@ async function onImportFileChange (event) {
|
|||||||
const stats = response?.data || {}
|
const stats = response?.data || {}
|
||||||
Notify.create({
|
Notify.create({
|
||||||
type: 'positive',
|
type: 'positive',
|
||||||
message: `CSV yuklendi. Islenen: ${stats.processed ?? importItems.length}, kaydedilen: ${stats.updated ?? importItems.length}, yeni aktiflestirilen: ${stats.activated_scope_count ?? 0}, hata: ${stats.error_count ?? 0}`
|
message: `CSV yuklendi. Islenen: ${stats.processed ?? importItems.length}, eslesen: ${stats.matched ?? stats.updated ?? importItems.length}, kaydedilen: ${stats.updated ?? importItems.length}, atlanan: ${stats.skipped ?? 0}, hata: ${stats.error_count ?? 0}`
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Notify.create({ type: 'negative', message: err?.message || 'CSV okunamadi' })
|
Notify.create({ type: 'negative', message: err?.message || 'CSV okunamadi' })
|
||||||
@@ -946,6 +950,7 @@ function copySelectedToSelected () {
|
|||||||
markDirty(target)
|
markDirty(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
copySelectedKeys.value = []
|
||||||
Notify.create({ type: 'positive', message: 'Kopyalama tamamlandi' })
|
Notify.create({ type: 'positive', message: 'Kopyalama tamamlandi' })
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1025,6 +1030,13 @@ function clearAllFilters () {
|
|||||||
numberRangeFilters.value = Object.fromEntries([...numericFields].map(field => [field, { min: '', max: '' }]))
|
numberRangeFilters.value = Object.fromEntries([...numericFields].map(field => [field, { min: '', max: '' }]))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function refreshRows () {
|
||||||
|
clearAllFilters()
|
||||||
|
selectedKeyMap.value = {}
|
||||||
|
copySelectedKeys.value = []
|
||||||
|
await loadRows()
|
||||||
|
}
|
||||||
|
|
||||||
async function loadRows () {
|
async function loadRows () {
|
||||||
if (emptyRetryTimer) {
|
if (emptyRetryTimer) {
|
||||||
clearTimeout(emptyRetryTimer)
|
clearTimeout(emptyRetryTimer)
|
||||||
@@ -1081,9 +1093,17 @@ async function saveSelected () {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(loadRows)
|
function resetTransientState () {
|
||||||
|
rows.value = []
|
||||||
|
selectedKeyMap.value = {}
|
||||||
|
copySelectedKeys.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(refreshRows)
|
||||||
|
onActivated(refreshRows)
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
if (emptyRetryTimer) clearTimeout(emptyRetryTimer)
|
if (emptyRetryTimer) clearTimeout(emptyRetryTimer)
|
||||||
|
resetTransientState()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user