Merge remote-tracking branch 'origin/master'

This commit is contained in:
M_Kececi
2026-04-02 13:36:09 +03:00
parent f5c91abafa
commit 4549152594
15 changed files with 318 additions and 626 deletions

View File

@@ -1,4 +1,4 @@
<template>
<template>
<!-- ===========================================================
🧾 ORDER ENTRY PAGE (BSSApp)
v23 Sticky-stack + Drawer uyumlu yapı
@@ -2928,8 +2928,8 @@ async function onColorChange(colorCode) {
const data = res?.data || []
if (Array.isArray(data) && data.length > 0) {
renkOptions2.value = data.map(x => ({
label: `${x.item_dim2_code || x.ItemDim2Code || ''} - ${x.color_description || x.ColorDescription || ''}`.trim(),
value: x.item_dim2_code || x.ItemDim2Code || ''
label: x.item_dim2_code,
value: x.item_dim2_code
}))
console.log('🎨 2. renk listesi yüklendi:', renkOptions2.value.length)
} else {

View File

@@ -160,7 +160,7 @@
flat
size="sm"
color="warning"
label="cdItem Bilgisi"
label="Urun Boyutlandirma"
@click="openCdItemDialog(props.row.NewItemCode)"
/>
<q-btn
@@ -184,11 +184,16 @@
:options="getColorOptions(props.row)"
option-label="colorLabel"
option-value="color_code"
use-input
fill-input
hide-selected
input-debounce="0"
emit-value
map-options
dense
filled
label="Yeni Renk"
:disable="isColorSelectionLocked(props.row)"
@update:model-value="() => onNewColorChange(props.row)"
/>
</q-td>
@@ -201,11 +206,16 @@
:options="getSecondColorOptions(props.row)"
option-label="item_dim2_label"
option-value="item_dim2_code"
use-input
fill-input
hide-selected
input-debounce="0"
emit-value
map-options
dense
filled
label="Yeni 2. Renk"
:disable="isColorSelectionLocked(props.row)"
/>
</q-td>
</template>
@@ -232,7 +242,7 @@
<q-dialog v-model="cdItemDialogOpen" persistent>
<q-card style="min-width: 980px; max-width: 98vw;">
<q-card-section class="row items-center q-pb-none">
<div class="text-h6">Yeni Kod cdItem Bilgileri</div>
<div class="text-h6">Urun Boyutlandirma</div>
<q-space />
<q-badge color="warning" text-color="black">
{{ cdItemTargetCode || '-' }}
@@ -242,60 +252,10 @@
<q-card-section class="q-pt-md">
<div class="row q-col-gutter-sm">
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ItemDimTypeCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemDimTypeCodes')" label="ItemDimTypeCode" />
<q-select v-model="cdItemDraftForm.ItemDimTypeCode" dense filled use-input fill-input hide-selected input-debounce="0" emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemDimTypeCodes')" label="Boyut Secenekleri" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ProductTypeCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('productTypeCodes')" label="ProductTypeCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ProductHierarchyID" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('productHierarchyIDs')" label="ProductHierarchyID" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.UnitOfMeasureCode1" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('unitOfMeasureCode1List')" label="UnitOfMeasureCode1" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ItemAccountGrCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemAccountGrCodes')" label="ItemAccountGrCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ItemTaxGrCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemTaxGrCodes')" label="ItemTaxGrCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ItemPaymentPlanGrCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemPaymentPlanGrCodes')" label="ItemPaymentPlanGrCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ItemDiscountGrCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemDiscountGrCodes')" label="ItemDiscountGrCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ItemVendorGrCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('itemVendorGrCodes')" label="ItemVendorGrCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.PromotionGroupCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('promotionGroupCodes')" label="PromotionGroupCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.ProductCollectionGrCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('productCollectionGrCodes')" label="ProductCollectionGrCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.StorePriceLevelCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('storePriceLevelCodes')" label="StorePriceLevelCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.PerceptionOfFashionCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('perceptionOfFashionCodes')" label="PerceptionOfFashionCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.CommercialRoleCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('commercialRoleCodes')" label="CommercialRoleCode" />
</div>
<div class="col-12 col-md-4">
<q-select v-model="cdItemDraftForm.StoreCapacityLevelCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('storeCapacityLevelCodes')" label="StoreCapacityLevelCode" />
</div>
<div class="col-12 col-md-6">
<q-select v-model="cdItemDraftForm.CustomsTariffNumberCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('customsTariffNumbers')" label="CustomsTariffNumberCode" />
</div>
<div class="col-12 col-md-6">
<q-select v-model="cdItemDraftForm.CompanyCode" dense filled emit-value map-options option-label="label" option-value="value" :options="lookupOptions('companyCodes')" label="CompanyCode" />
<q-select v-model="cdItemDraftForm.ProductHierarchyID" dense filled use-input fill-input hide-selected input-debounce="0" emit-value map-options option-label="label" option-value="value" :options="lookupOptions('productHierarchyIDs')" label="Urun Hiyerarsi Grubu" />
</div>
</div>
</q-card-section>
@@ -332,6 +292,11 @@
v-model="row.AttributeCode"
dense
filled
use-input
fill-input
hide-selected
input-debounce="0"
@filter="(val, update) => onFilterAttributeOption(row, val, update)"
emit-value
map-options
option-label="label"
@@ -363,8 +328,8 @@ import { normalizeSearchText } from 'src/utils/searchText'
const route = useRoute()
const $q = useQuasar()
const store = useOrderProductionItemStore()
const BAGGI_CODE_PATTERN = /^[A-Z][0-9]{3}-[A-Z]{2,3}[0-9]{5}$/
const BAGGI_CODE_ERROR = 'Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XX99999'
const BAGGI_CODE_PATTERN = /^[A-Z][0-9]{3}-[A-Z]{3}[0-9]{5}$/
const BAGGI_CODE_ERROR = 'Girdiginiz kod BAGGI kod sistemine uyumlu degil. Format: X999-XXX99999'
const orderHeaderID = computed(() => String(route.params.orderHeaderID || '').trim())
const header = computed(() => store.header || {})
@@ -446,12 +411,11 @@ const newItemEntryModeOptions = [
{ label: 'Eski Kod Sec', value: 'selected' },
{ label: 'Yeni Kod Ekle', value: 'typed' }
]
const productCodeSelectOptions = computed(() =>
(productOptions.value || []).map(p => {
const code = String(p?.ProductCode || '').trim().toUpperCase()
return { label: code, value: code }
}).filter(x => !!x.value)
}).filter(x => !!x.value && x.value.length === 13)
)
function applyNewItemVisualState (row, source = 'typed') {
@@ -494,18 +458,49 @@ function onSelectProduct (row, code) {
}
function onNewItemEntryModeChange (row, mode) {
const next = String(mode || '').trim()
row.NewItemEntryMode = next
row.NewItemEntryMode = String(mode || '').trim()
row.NewItemCode = ''
row.NewItemMode = 'empty'
row.NewItemSource = ''
row.NewColor = ''
row.NewDim2 = ''
row.NewItemMode = 'empty'
row.NewItemSource = ''
}
function escapeRegExp (value) {
return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
}
function buildNextCodeFromPrefix (prefix) {
const normalizedPrefix = String(prefix || '').trim().toUpperCase()
if (!/^[A-Z][0-9]{3}-[A-Z]{3}$/.test(normalizedPrefix)) return ''
const codeRegex = new RegExp(`^${escapeRegExp(normalizedPrefix)}(\\d{5})$`)
let maxSuffix = 0
for (const p of (productOptions.value || [])) {
const code = String(p?.ProductCode || '').trim().toUpperCase()
const m = code.match(codeRegex)
if (!m) continue
const n = Number(m[1] || 0)
if (Number.isFinite(n) && n > maxSuffix) maxSuffix = n
}
const next = maxSuffix > 0 ? maxSuffix + 1 : 1
return `${normalizedPrefix}${String(next).padStart(5, '0')}`
}
function onNewItemChange (row, val, source = 'typed') {
const prevCode = String(row?.NewItemCode || '').trim().toUpperCase()
const next = String(val || '').trim().toUpperCase()
let next = String(val || '').trim().toUpperCase()
if (source === 'typed' && row?.NewItemEntryMode === 'typed' && /^[A-Z][0-9]{3}-[A-Z]{3}$/.test(next)) {
const autoCode = buildNextCodeFromPrefix(next)
if (autoCode) {
next = autoCode
$q.notify({ type: 'info', message: `Yeni kod otomatik tamamlandi: ${autoCode}` })
}
}
if (next.length > 13) {
$q.notify({ type: 'negative', message: 'Model kodu en fazla 13 karakter olabilir.' })
row.NewItemCode = next.slice(0, 13)
@@ -526,7 +521,39 @@ function onNewItemChange (row, val, source = 'typed') {
store.fetchColors(row.NewItemCode)
}
if (row.NewItemMode === 'new' && isValidBaggiModelCode(row.NewItemCode) && row.NewItemCode !== prevCode) {
openCdItemDialog(row.NewItemCode)
openNewCodeSetupFlow(row.NewItemCode)
}
}
function isAttributeDraftComplete (rows) {
if (!Array.isArray(rows) || !rows.length) return false
return rows.every(r => String(r?.AttributeCode || '').trim())
}
function isNewCodeSetupComplete (itemCode) {
const code = String(itemCode || '').trim().toUpperCase()
if (!code) return false
const hasCdItem = !!store.getCdItemDraft(code)
const attrRows = store.getProductAttributeDraft(code)
return hasCdItem && isAttributeDraftComplete(attrRows)
}
function isColorSelectionLocked (row) {
const code = String(row?.NewItemCode || '').trim().toUpperCase()
if (!code) return true
if (row?.NewItemMode !== 'new') return false
return !isNewCodeSetupComplete(code)
}
function openNewCodeSetupFlow (itemCode) {
const code = String(itemCode || '').trim().toUpperCase()
if (!code) return
if (!store.getCdItemDraft(code)) {
openCdItemDialog(code)
return
}
if (!isAttributeDraftComplete(store.getProductAttributeDraft(code))) {
openAttributeDialog(code)
}
}
@@ -543,7 +570,7 @@ function getColorOptions (row) {
const list = store.colorOptionsByCode[code] || []
return list.map(c => ({
...c,
colorLabel: `${c.color_code || c.ColorCode || ''} - ${c.color_description || c.ColorDescription || ''}`.trim()
colorLabel: `${c.color_code} - ${c.color_description || ''}`.trim()
}))
}
@@ -554,8 +581,7 @@ function getSecondColorOptions (row) {
const list = store.secondColorOptionsByKey[key] || []
return list.map(c => ({
...c,
item_dim2_code: c.item_dim2_code || c.ItemDim2Code || '',
item_dim2_label: `${c.item_dim2_code || c.ItemDim2Code || ''} - ${c.color_description || c.ColorDescription || ''}`.trim()
item_dim2_label: `${c.item_dim2_code} - ${c.color_description || ''}`.trim()
}))
}
@@ -637,36 +663,64 @@ function createEmptyCdItemDraft (itemCode) {
return {
ItemTypeCode: '1',
ItemCode: String(itemCode || '').trim().toUpperCase(),
ItemDimTypeCode: '',
ProductTypeCode: '',
ItemDimTypeCode: '1',
ProductTypeCode: '1',
ProductHierarchyID: '',
UnitOfMeasureCode1: '',
UnitOfMeasureCode1: 'AD',
ItemAccountGrCode: '',
ItemTaxGrCode: '',
ItemTaxGrCode: '10%',
ItemPaymentPlanGrCode: '',
ItemDiscountGrCode: '',
ItemVendorGrCode: '',
PromotionGroupCode: '',
ProductCollectionGrCode: '',
StorePriceLevelCode: '',
PerceptionOfFashionCode: '',
CommercialRoleCode: '',
ProductCollectionGrCode: '0',
StorePriceLevelCode: '0',
PerceptionOfFashionCode: '0',
CommercialRoleCode: '0',
StoreCapacityLevelCode: '',
CustomsTariffNumberCode: '',
CompanyCode: ''
CompanyCode: '1'
}
}
function lookupOptions (key) {
if (key === 'itemDimTypeCodes') {
return [
{ value: '1', label: '1 - RENK' },
{ value: '2', label: '2 - RENK-BEDEN' },
{ value: '3', label: '3 - RENK-BEDEN-YAKA' }
]
}
const list = store.cdItemLookups?.[key] || []
return list.map(x => {
const code = String(x?.code || '').trim()
const desc = String(x?.description || '').trim()
return {
value: code,
label: desc ? `${code} - ${desc}` : code
}
})
return list
.map(x => {
const code = String(x?.code || '').trim()
const desc = String(x?.description || '').trim()
return {
value: code,
label: desc ? `${code} - ${desc}` : code,
_desc: desc
}
})
.filter(opt => !isDummyLookupOption(key, opt.value, opt._desc))
.map(({ value, label }) => ({ value, label }))
}
function isDummyLookupOption (key, codeRaw, descRaw) {
const code = String(codeRaw || '').trim().toUpperCase()
const desc = String(descRaw || '').trim().toUpperCase()
if (!code) return true
if (code === '0' || code === '00' || code === '000' || code === '0000') return true
if (desc.includes('DUMMY')) return true
// Is plani dokumanindaki sari/default alanlar
if (key === 'unitOfMeasureCode1List' && code === 'AD') return true
if (key === 'itemTaxGrCodes' && code === '10%') return true
if (key === 'companyCodes' && code === '1') return true
return false
}
async function openCdItemDialog (itemCode) {
@@ -700,27 +754,27 @@ function normalizeCdItemDraftForPayload (draftRaw) {
return {
ItemTypeCode: toIntOrNil(d.ItemTypeCode) || 1,
ItemCode: String(d.ItemCode || '').trim().toUpperCase(),
ItemDimTypeCode: toIntOrNil(d.ItemDimTypeCode),
ProductTypeCode: toIntOrNil(d.ProductTypeCode),
ItemDimTypeCode: toIntOrNil(d.ItemDimTypeCode) || 1,
ProductTypeCode: 1,
ProductHierarchyID: toIntOrNil(d.ProductHierarchyID),
UnitOfMeasureCode1: toStrOrNil(d.UnitOfMeasureCode1),
ItemAccountGrCode: toStrOrNil(d.ItemAccountGrCode),
ItemTaxGrCode: toStrOrNil(d.ItemTaxGrCode),
ItemPaymentPlanGrCode: toStrOrNil(d.ItemPaymentPlanGrCode),
ItemDiscountGrCode: toStrOrNil(d.ItemDiscountGrCode),
ItemVendorGrCode: toStrOrNil(d.ItemVendorGrCode),
PromotionGroupCode: toStrOrNil(d.PromotionGroupCode),
ProductCollectionGrCode: toStrOrNil(d.ProductCollectionGrCode),
StorePriceLevelCode: toStrOrNil(d.StorePriceLevelCode),
PerceptionOfFashionCode: toStrOrNil(d.PerceptionOfFashionCode),
CommercialRoleCode: toStrOrNil(d.CommercialRoleCode),
StoreCapacityLevelCode: toStrOrNil(d.StoreCapacityLevelCode),
CustomsTariffNumberCode: toStrOrNil(d.CustomsTariffNumberCode),
CompanyCode: toStrOrNil(d.CompanyCode)
UnitOfMeasureCode1: 'AD',
ItemAccountGrCode: null,
ItemTaxGrCode: '10%',
ItemPaymentPlanGrCode: null,
ItemDiscountGrCode: null,
ItemVendorGrCode: null,
PromotionGroupCode: null,
ProductCollectionGrCode: '0',
StorePriceLevelCode: '0',
PerceptionOfFashionCode: '0',
CommercialRoleCode: '0',
StoreCapacityLevelCode: null,
CustomsTariffNumberCode: null,
CompanyCode: '1'
}
}
function saveCdItemDraft () {
async function saveCdItemDraft () {
const payload = normalizeCdItemDraftForPayload(cdItemDraftForm.value)
if (!payload.ItemCode) {
$q.notify({ type: 'negative', message: 'ItemCode bos olamaz.' })
@@ -728,31 +782,7 @@ function saveCdItemDraft () {
}
store.setCdItemDraft(payload.ItemCode, payload)
cdItemDialogOpen.value = false
}
function createDummyAttributeRows () {
const sharedOptions = [
{ value: 'DAMATLIK', label: 'DAMATLIK - DAMATLIK' },
{ value: 'TAKIM', label: 'TAKIM - TAKIM ELBISE' },
{ value: 'CEKET', label: 'CEKET - CEKET' },
{ value: 'PANTOLON', label: 'PANTOLON - PANTOLON' }
]
const rows = [{
AttributeTypeCodeNumber: 1,
TypeLabel: '1-001 Urun Ana Grubu',
AttributeCode: '',
Options: sharedOptions
}]
for (let i = 2; i <= 50; i++) {
const code = String(i).padStart(3, '0')
rows.push({
AttributeTypeCodeNumber: i,
TypeLabel: `1-${code} Dummy Ozellik ${i}`,
AttributeCode: '',
Options: sharedOptions
})
}
return rows
await openAttributeDialog(payload.ItemCode)
}
function buildAttributeRowsFromLookup (list) {
@@ -780,13 +810,31 @@ function buildAttributeRowsFromLookup (list) {
.sort((a, b) => a.typeCode - b.typeCode)
.map(g => ({
AttributeTypeCodeNumber: g.typeCode,
TypeLabel: `${g.typeCode}-${g.typeDesc}`,
TypeLabel: `${g.typeCode} - ${g.typeDesc}`,
AttributeCode: '',
Options: g.options
AllOptions: [...g.options],
Options: [...g.options]
}))
return rows
}
function onFilterAttributeOption (row, val, update) {
const raw = String(val || '')
const needle = normalizeSearchText(raw)
const base = Array.isArray(row?.AllOptions) ? row.AllOptions : (Array.isArray(row?.Options) ? row.Options : [])
update(() => {
if (!needle) {
row.Options = [...base]
return
}
row.Options = base.filter(opt => {
const label = normalizeSearchText(opt?.label || '')
const value = normalizeSearchText(opt?.value || '')
return label.includes(needle) || value.includes(needle)
})
})
}
async function openAttributeDialog (itemCode) {
const code = String(itemCode || '').trim().toUpperCase()
if (!code) return
@@ -794,10 +842,22 @@ async function openAttributeDialog (itemCode) {
const existing = store.getProductAttributeDraft(code)
const fetched = await store.fetchProductAttributes(1)
const fromLookup = buildAttributeRowsFromLookup(fetched)
const baseRows = fromLookup.length ? fromLookup : createDummyAttributeRows()
if (!fromLookup.length) {
$q.notify({ type: 'negative', message: 'Urun ozellikleri listesi alinamadi. Lutfen daha sonra tekrar deneyin.' })
return
}
const baseRows = fromLookup
attributeRows.value = Array.isArray(existing) && existing.length
? JSON.parse(JSON.stringify(existing))
: baseRows
for (const row of (attributeRows.value || [])) {
if (!Array.isArray(row.AllOptions)) {
row.AllOptions = Array.isArray(row.Options) ? [...row.Options] : []
}
if (!Array.isArray(row.Options)) {
row.Options = [...row.AllOptions]
}
}
attributeDialogOpen.value = true
}
@@ -945,15 +1005,15 @@ function groupItems (items, prevRows = []) {
const prevMap = new Map()
for (const r of prevRows || []) {
if (!r?.RowKey) continue
prevMap.set(r.RowKey, {
NewDesc: String(r.NewDesc || '').trim(),
NewItemCode: String(r.NewItemCode || '').trim().toUpperCase(),
NewColor: String(r.NewColor || '').trim().toUpperCase(),
NewDim2: String(r.NewDim2 || '').trim().toUpperCase(),
NewItemMode: String(r.NewItemMode || '').trim(),
NewItemSource: String(r.NewItemSource || '').trim(),
NewItemEntryMode: String(r.NewItemEntryMode || '').trim()
})
prevMap.set(r.RowKey, {
NewDesc: String(r.NewDesc || '').trim(),
NewItemCode: String(r.NewItemCode || '').trim().toUpperCase(),
NewColor: String(r.NewColor || '').trim().toUpperCase(),
NewDim2: String(r.NewDim2 || '').trim().toUpperCase(),
NewItemMode: String(r.NewItemMode || '').trim(),
NewItemSource: String(r.NewItemSource || '').trim(),
NewItemEntryMode: String(r.NewItemEntryMode || '').trim()
})
}
const map = new Map()

View File

@@ -182,7 +182,15 @@ export const useOrderProductionItemStore = defineStore('orderproductionitems', {
this.cdItemLookups = res?.data || null
return this.cdItemLookups
} catch (err) {
this.error = err?.response?.data || err?.message || 'cdItem lookup listesi alinamadi'
const rid =
err?.response?.headers?.['x-debug-request-id'] ||
err?.response?.data?.requestId ||
''
logApiError('fetchCdItemLookups', err, { force, requestId: rid })
if (rid) {
console.error(`[OrderProductionItemStore] fetchCdItemLookups requestId=${rid}`)
}
this.error = extractApiErrorMessage(err, 'cdItem lookup listesi alinamadi')
return null
}
},

View File

@@ -1899,7 +1899,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
const girilen = Number(form.fiyat || 0)
if (minFiyat > 0 && girilen < minFiyat && $q) {
if (minFiyat > 0 && girilen > 0 && girilen < minFiyat && $q) {
fiyatOK = await new Promise(resolve => {
$q.dialog({
title: 'Fiyat Uyarısı',
@@ -3644,16 +3644,7 @@ export const useOrderEntryStore = defineStore('orderentry', {
continue
}
// AKSBIR / bedensiz satırda kaynak adet bazen `row.adet`te tutuluyor.
// Bu fallback yoksa ItemDim1Code='' satırı Qty1=0 düşebiliyor.
const qty = toNum(
row.qty ??
row.Qty1 ??
row.miktar ??
row.adet ??
row.Adet ??
0
)
const qty = toNum(row.qty ?? row.Qty1 ?? row.miktar ?? 0)
// ✅ ComboKey stabil: bedenKey = '_'
const bedenKey = '_'