b2b.systemd

This commit is contained in:
2026-02-12 13:18:27 +03:00
parent b7346a4bfa
commit 4d8e5e00d5
4 changed files with 135 additions and 24 deletions

View File

@@ -3,7 +3,10 @@
🧾 ORDER ENTRY PAGE (BSSApp) 🧾 ORDER ENTRY PAGE (BSSApp)
v23 Sticky-stack + Drawer uyumlu yapı v23 Sticky-stack + Drawer uyumlu yapı
============================================================ --> ============================================================ -->
<q-page class="order-page"> <q-page
v-if="canReadOrder"
class="order-page"
>
<!-- 🔄 SAYFA LOADERI --> <!-- 🔄 SAYFA LOADERI -->
<q-inner-loading :showing="loadingHeader || loadingCari || loadingModels" color="primary"> <q-inner-loading :showing="loadingHeader || loadingCari || loadingModels" color="primary">
<q-spinner size="50px" /> <q-spinner size="50px" />
@@ -210,30 +213,32 @@
<div class="text-subtitle2 text-weight-bold">Sipariş Formu</div> <div class="text-subtitle2 text-weight-bold">Sipariş Formu</div>
<div> <div>
<q-btn <q-btn
v-if="isViewOnly" v-if="isViewOnly && canReadOrder"
label="🖨 SİPARİŞİ YAZDIR" label="🖨 SİPARİŞİ YAZDIR"
color="primary" color="primary"
icon="print" icon="print"
class="q-ml-sm" class="q-ml-sm"
@click="orderStore.downloadOrderPdf()" @click="onPrintOrder"
/> />
<q-btn <q-btn
v-else v-else-if="canSubmitOrder"
:label="isEditMode ? 'TÜMÜNÜ GÜNCELLE' : 'TÜMÜNÜ KAYDET'" :label="isEditMode ? 'TÜMÜNÜ GÜNCELLE' : 'TÜMÜNÜ KAYDET'"
color="primary" color="primary"
icon="save" icon="save"
class="q-ml-sm" class="q-ml-sm"
:loading="orderStore.loading" :loading="orderStore.loading"
:disable="!canSubmitOrder"
@click="confirmAndSubmit" @click="confirmAndSubmit"
/> />
<q-btn <q-btn
label="YENİ SİPARİŞ" label="YENİ SİPARİŞ"
v-if="canWriteOrder"
color="secondary" color="secondary"
icon="add_circle" icon="add_circle"
class="q-ml-sm" class="q-ml-sm"
@click="resetEditor" @click="onResetEditorClick"
:disable="isClosedRow" :disable="isClosedRow || !canWriteOrder"
/> />
</div> </div>
</div> </div>
@@ -532,12 +537,12 @@
<div class="col-2 q-mt-sm"> <div class="col-2 q-mt-sm">
<q-btn <q-btn
v-if="selectedSeriSet" v-if="selectedSeriSet && canMutateRows"
color="primary" color="primary"
icon="add" icon="add"
label="Seri Ekle" label="Seri Ekle"
@click="applySeriSet" @click="applySeriSet"
:disable="isClosedRow || isViewOnly" :disable="isClosedRow || isViewOnly || !canMutateRows"
:readonly="isViewOnly" :readonly="isViewOnly"
/> />
</div> </div>
@@ -674,26 +679,28 @@
<div class="row justify-between items-center q-mt-md"> <div class="row justify-between items-center q-mt-md">
<div class="row q-gutter-sm"> <div class="row q-gutter-sm">
<q-btn <q-btn
v-if="canMutateRows"
:color="isEditing ? 'positive' : 'primary'" :color="isEditing ? 'positive' : 'primary'"
:label="isEditing ? 'Güncelle' : 'Kaydet'" :label="isEditing ? 'Güncelle' : 'Kaydet'"
@click="onSaveOrUpdateRow" @click="onSaveOrUpdateRow"
:disable="isClosedRow || isViewOnly" :disable="isClosedRow || isViewOnly || !canMutateRows"
/> />
<q-btn <q-btn
v-if="isEditing" v-if="isEditing && canMutateRows"
color="negative" color="negative"
flat flat
label="Satırı Sil" label="Satırı Sil"
@click="removeSelected" @click="removeSelected"
:disable="isClosedRow || isViewOnly" :disable="isClosedRow || isViewOnly || !canMutateRows"
/> />
<q-btn <q-btn
v-if="canMutateRows"
flat flat
color="grey-8" color="grey-8"
label="Formu Temizle" label="Formu Temizle"
@click="resetEditor" @click="onResetEditorClick"
:disable="isClosedRow||isViewOnly" :disable="isClosedRow||isViewOnly || !canMutateRows"
/> />
</div> </div>
@@ -734,6 +741,14 @@
</div> <!-- ✅ order-scroll-y --> </div> <!-- ✅ order-scroll-y -->
</q-page> </q-page>
<q-page
v-else
class="order-page flex flex-center"
>
<div class="text-negative text-subtitle1">
Bu module erisim yetkiniz yok.
</div>
</q-page>
</template> </template>
<script setup> <script setup>
@@ -802,6 +817,15 @@ const productCache = reactive({})
const confirmAndSubmit = async () => { const confirmAndSubmit = async () => {
if (orderStore.loading) return if (orderStore.loading) return
if (!hasSubmitPermission()) {
notifyNoPermission(
isEditMode.value
? 'Siparis guncelleme yetkiniz yok'
: 'Siparis kaydetme yetkiniz yok'
)
return
}
// Grid boşsa // Grid boşsa
if (!orderStore.summaryRows?.length) { if (!orderStore.summaryRows?.length) {
$q.notify({ $q.notify({
@@ -838,6 +862,47 @@ const defaultOlusturmaTarihi = today.toISOString().substring(0, 10)
const defaultTerminTarihi = Termindate.toISOString().substring(0, 10) const defaultTerminTarihi = Termindate.toISOString().substring(0, 10)
const isEditMode = computed(() => orderStore.mode === 'edit') const isEditMode = computed(() => orderStore.mode === 'edit')
const canSubmitOrder = computed(() => {
if (isViewOnly.value) return false
return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
})
const canMutateRows = computed(() => {
if (isViewOnly.value) return false
return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
})
function notifyNoPermission(message) {
$q.notify({
type: 'negative',
message
})
}
function hasSubmitPermission() {
if (isViewOnly.value) return false
return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
}
function hasRowMutationPermission() {
if (isViewOnly.value) return false
return isEditMode.value ? canUpdateOrder.value : canWriteOrder.value
}
function onPrintOrder() {
if (!canReadOrder.value) {
notifyNoPermission('Siparisi goruntuleme yetkiniz yok')
return
}
orderStore.downloadOrderPdf()
}
async function onResetEditorClick() {
if (!canWriteOrder.value) {
notifyNoPermission('Yeni siparis baslatma yetkiniz yok')
return
}
await resetEditor()
}
/* =========================================================== /* ===========================================================
🔹 FORM NESNESİ — TEMEL ALANLAR 🔹 FORM NESNESİ — TEMEL ALANLAR
@@ -1929,6 +1994,15 @@ function scrollToRow(clientKey) {
🔹 SERİ SETİ UYGULAMA (FINAL — grpKey SAFE) 🔹 SERİ SETİ UYGULAMA (FINAL — grpKey SAFE)
=========================================================== */ =========================================================== */
function applySeriSet() { function applySeriSet() {
if (!hasRowMutationPermission()) {
notifyNoPermission(
isEditMode.value
? 'Siparis satiri guncelleme yetkiniz yok'
: 'Siparis satiri ekleme yetkiniz yok'
)
return
}
if (!selectedSeriSet.value) return if (!selectedSeriSet.value) return
/* 🔑 TEK KAYNAK */ /* 🔑 TEK KAYNAK */
@@ -1984,6 +2058,11 @@ function updateTotals(f) {
function removeSelected() { function removeSelected() {
if (!hasRowMutationPermission()) {
notifyNoPermission('Siparis satiri silme/guncelleme yetkiniz yok')
return
}
const row = selectedRow.value const row = selectedRow.value
if (!row) { if (!row) {
$q.notify({ type: 'warning', message: 'Silmek için önce bir satır seçmelisiniz.' }) $q.notify({ type: 'warning', message: 'Silmek için önce bir satır seçmelisiniz.' })
@@ -2666,6 +2745,15 @@ async function onColor2Change(colorCode2) {
const bedenStock = ref([]) // Görsel tablo için stok listesi const bedenStock = ref([]) // Görsel tablo için stok listesi
const stockMap = ref({}) // { "48": 12, "50": 7, ... } şeklinde key-value map const stockMap = ref({}) // { "48": 12, "50": 7, ... } şeklinde key-value map
const onSaveOrUpdateRow = async () => { const onSaveOrUpdateRow = async () => {
if (!hasRowMutationPermission()) {
notifyNoPermission(
isEditMode.value
? 'Siparis satiri guncelleme yetkiniz yok'
: 'Siparis satiri kaydetme yetkiniz yok'
)
return
}
await orderStore.saveOrUpdateRowUnified({ await orderStore.saveOrUpdateRowUnified({
form, form,

View File

@@ -10,7 +10,7 @@
<!-- 🟡 TASLAK --> <!-- 🟡 TASLAK -->
<div <div
v-if="hasDraft && canUpdateOrder" v-if="hasDraft && canWriteOrder"
class="draft-card q-pa-lg rounded-borders shadow-2 bg-white" class="draft-card q-pa-lg rounded-borders shadow-2 bg-white"
> >
<div class="text-subtitle1 text-bold text-negative"> <div class="text-subtitle1 text-bold text-negative">
@@ -31,7 +31,7 @@
color="primary" color="primary"
icon="login" icon="login"
label="TASLAĞA DEVAM ET" label="TASLAĞA DEVAM ET"
:disable="!canUpdateOrder" :disable="!canWriteOrder"
@click="continueDraft" @click="continueDraft"
/> />
</div> </div>
@@ -80,11 +80,10 @@ import { useQuasar } from 'quasar'
import { useOrderEntryStore } from 'src/stores/orderentryStore' import { useOrderEntryStore } from 'src/stores/orderentryStore'
import { usePermission } from 'src/composables/usePermission' import { usePermission } from 'src/composables/usePermission'
const { canRead, canWrite, canUpdate } = usePermission() const { canRead, canWrite } = usePermission()
const canReadOrder = canRead('order') const canReadOrder = canRead('order')
const canWriteOrder = canWrite('order') const canWriteOrder = canWrite('order')
const canUpdateOrder = canUpdate('order')
const router = useRouter() const router = useRouter()
const $q = useQuasar() const $q = useQuasar()
@@ -130,7 +129,7 @@ const draftNumber = computed(() => {
function continueDraft () { function continueDraft () {
if (!canUpdateOrder.value) { if (!canWriteOrder.value) {
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
message: 'Taslak güncelleme yetkiniz yok' message: 'Taslak güncelleme yetkiniz yok'

View File

@@ -2,6 +2,25 @@ import { defineStore } from 'pinia'
import api from 'src/services/api' import api from 'src/services/api'
import { useAuthStore } from 'stores/authStore' import { useAuthStore } from 'stores/authStore'
const ACTION_ALIASES = {
read: ['read', 'view'],
view: ['read', 'view'],
write: ['write', 'insert'],
insert: ['write', 'insert'],
update: ['update'],
delete: ['delete'],
export: ['export']
}
function normalizeToken (value) {
return String(value || '').trim().toLowerCase()
}
function actionCandidates (action) {
const key = normalizeToken(action)
return ACTION_ALIASES[key] || [key]
}
export const usePermissionStore = defineStore('permission', { export const usePermissionStore = defineStore('permission', {
@@ -44,11 +63,13 @@ export const usePermissionStore = defineStore('permission', {
if (apiPathOrPerm.includes(':')) { if (apiPathOrPerm.includes(':')) {
const [module, action] = apiPathOrPerm.split(':') const [moduleRaw, actionRaw] = apiPathOrPerm.split(':')
const module = normalizeToken(moduleRaw)
const actions = actionCandidates(actionRaw)
return state.matrix.some(p => return state.matrix.some(p =>
p.module === module && normalizeToken(p.module) === module &&
p.action === action && actions.includes(normalizeToken(p.action)) &&
p.allowed === true p.allowed === true
) )
} }
@@ -94,12 +115,13 @@ export const usePermissionStore = defineStore('permission', {
hasModule: (state) => (module) => { hasModule: (state) => (module) => {
const auth = useAuthStore() const auth = useAuthStore()
const moduleKey = normalizeToken(module)
if (auth.isAdmin) return true if (auth.isAdmin) return true
if (!state.loaded) return false if (!state.loaded) return false
return state.matrix.some(p => return state.matrix.some(p =>
p.module === module && normalizeToken(p.module) === moduleKey &&
p.allowed === true p.allowed === true
) )
}, },
@@ -110,13 +132,15 @@ export const usePermissionStore = defineStore('permission', {
hasPermission: (state) => (module, action) => { hasPermission: (state) => (module, action) => {
const auth = useAuthStore() const auth = useAuthStore()
const moduleKey = normalizeToken(module)
const actions = actionCandidates(action)
if (auth.isAdmin) return true if (auth.isAdmin) return true
if (!state.loaded) return false if (!state.loaded) return false
return state.matrix.some(p => return state.matrix.some(p =>
p.module === module && normalizeToken(p.module) === moduleKey &&
p.action === action && actions.includes(normalizeToken(p.action)) &&
p.allowed === true p.allowed === true
) )
} }