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

View File

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

View File

@@ -2,6 +2,25 @@ import { defineStore } from 'pinia'
import api from 'src/services/api'
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', {
@@ -44,11 +63,13 @@ export const usePermissionStore = defineStore('permission', {
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 =>
p.module === module &&
p.action === action &&
normalizeToken(p.module) === module &&
actions.includes(normalizeToken(p.action)) &&
p.allowed === true
)
}
@@ -94,12 +115,13 @@ export const usePermissionStore = defineStore('permission', {
hasModule: (state) => (module) => {
const auth = useAuthStore()
const moduleKey = normalizeToken(module)
if (auth.isAdmin) return true
if (!state.loaded) return false
return state.matrix.some(p =>
p.module === module &&
normalizeToken(p.module) === moduleKey &&
p.allowed === true
)
},
@@ -110,13 +132,15 @@ export const usePermissionStore = defineStore('permission', {
hasPermission: (state) => (module, action) => {
const auth = useAuthStore()
const moduleKey = normalizeToken(module)
const actions = actionCandidates(action)
if (auth.isAdmin) return true
if (!state.loaded) return false
return state.matrix.some(p =>
p.module === module &&
p.action === action &&
normalizeToken(p.module) === moduleKey &&
actions.includes(normalizeToken(p.action)) &&
p.allowed === true
)
}