fix(marketplace): убрать as never/as number из Стола администратора

Аудит кастов по Столу оператора (ПВЗ) и Столу администратора:
- template-кастов 0, `as any` 0 — опасных/рантайм-фатальных нет;
- два реальных хака в admin-страницах убраны без каста:
  - BoardAgendaWriteoff: `(... ) as never` — локальный строковый WriteoffStatus
    не стыковался с SDK-входом statuses (enum MarketplaceWriteoffProposalStatus).
    Добавлена явная таблица STATUS_TO_ENUM (5 видимых совету статусов → enum),
    statuses строится map'ом. Тип доказан образцом соседней AdminWriteoffsPage,
    которая так же кормит listWriteoffProposals enum-массивом.
  - OnboardingCoopAcceptCpp/composable: `stepToRegistryId[meta.id] as number` →
    typeof-guard (робастно к noUncheckedIndexedAccess).

НЕ тронуты api-слойные `return result as <ViewType>` (OperatorIssuance/Reception/
InventoryLabeling/ReturnClaims, AdminWriteoffs): это НЕСУЩЕЕ сужение Zeus-скаляров
(DateTime/ID типизированы Zeus как unknown; view-тип сужает их до string/
NonNullable через Omit&{…}). Убрать = сломать типы (unknown не присваивается
string) или добавить бессмысленный рантайм-.map(). Направление каста безопасное
(unknown→точный тип), у каждого инлайн-обоснование. Это не `as any`.

ESLint 0.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ant
2026-05-30 05:46:08 +00:00
parent 49834fb58b
commit f9faa45844
2 changed files with 19 additions and 6 deletions
@@ -1,6 +1,7 @@
<script lang="ts" setup>
import { computed, onMounted, onUnmounted, ref } from 'vue';
import { Notify } from 'quasar';
import { Zeus } from '@coopenomics/sdk';
import {
listWriteoffProposals,
type MarketplaceWriteoffProposalsPageView,
@@ -32,6 +33,17 @@ const BOARD_STATUSES: WriteoffStatus[] = [
'REJECTED',
];
// Локальный WriteoffStatus — строковый union (5 видимых совету статусов).
// SDK-вход statuses ждёт enum MarketplaceWriteoffProposalStatus — маппим через
// явную таблицу, чтобы не кастовать и не плодить DRAFT в records/шаблоне.
const STATUS_TO_ENUM: Record<WriteoffStatus, Zeus.MarketplaceWriteoffProposalStatus> = {
ON_AGENDA: Zeus.MarketplaceWriteoffProposalStatus.ON_AGENDA,
AUTHORIZED: Zeus.MarketplaceWriteoffProposalStatus.AUTHORIZED,
EXECUTING: Zeus.MarketplaceWriteoffProposalStatus.EXECUTING,
EXECUTED: Zeus.MarketplaceWriteoffProposalStatus.EXECUTED,
REJECTED: Zeus.MarketplaceWriteoffProposalStatus.REJECTED,
};
const STATUS_LABEL: Record<WriteoffStatus, string> = {
ON_AGENDA: 'На повестке',
AUTHORIZED: 'Одобрено',
@@ -59,7 +71,8 @@ const total = computed(() => page.value?.totalCount ?? 0);
async function load(): Promise<void> {
loading.value = true;
try {
const statuses = (statusFilter.value ? [statusFilter.value] : BOARD_STATUSES) as never;
const selected = statusFilter.value ? [statusFilter.value] : BOARD_STATUSES;
const statuses = selected.map((s) => STATUS_TO_ENUM[s]);
page.value = await listWriteoffProposals(
{ statuses },
{ page: 1, limit: 100 },
@@ -129,11 +129,11 @@ export const useMarketplaceOnboarding = () => {
const entries = await Promise.all(
STEP_META.map(async (meta) => {
try {
const doc = await generateDocument(
coopname,
username,
stepToRegistryId[meta.id] as number,
);
const registryId = stepToRegistryId[meta.id];
if (typeof registryId !== 'number') {
return [meta.id, ''] as const;
}
const doc = await generateDocument(coopname, username, registryId);
return [meta.id, doc.html] as const;
} catch {
return [meta.id, ''] as const;