import fs from 'node:fs' const read=p=>fs.readFileSync(p,'utf8') const failures=[] const contract=read('packages/kbx-contracts/src/grid.ts') for(const token of ['KbxGridEditingPolicy','KbxGridPasteResult','KbxBulkSelectionRequest',"'all-filtered'",'drilldown'])if(!contract.includes(token))failures.push(`grid contract missing ${token}`) const utility=read('packages/kbx-ui/src/grid/editing.ts') for(const token of ['normalizeKbxGridClipboardData','normalizeKbxGridClipboardValue','toKbxBulkSelectionRequest','INVALID_NUMBER','INVALID_DATE','INVALID_BOOLEAN'])if(!utility.includes(token))failures.push(`grid editing utility missing ${token}`) const grid=read('packages/kbx-ui/src/components/KbxDataGrid.vue') for(const token of ['allowAllFilteredSelection','selectionStateChanged','rowAddRequested','rowDuplicateRequested','fillDownApplied','pasteProcessed','errorFocusChanged','contextRequested','drillDownRequested','processClipboardData','검색결과','첫/다음 오류'])if(!grid.includes(token))failures.push(`KbxDataGrid productivity missing ${token}`) const fast=read('packages/kbx-ui/src/components/KbxFastEntryPage.vue') for(const token of ['Ctrl+V','Enter','F2','오류는 Grid에서 바로 이동'])if(!fast.includes(token))failures.push(`KbxFastEntryPage guide missing ${token}`) const orderRegister=read('apps/web/src/modules/oms/orders/register/OrderRegisterPage.vue') for(const token of ['selection="multiple"','allowRowAdd:true','allowRowDuplicate:true','fillDown:true','paste:true','errorNavigation:true','row-add-requested','row-duplicate-requested'])if(!orderRegister.includes(token))failures.push(`OMS-ORD-002 fast-entry adoption missing ${token}`) const orderSearchVm=read('apps/web/src/modules/oms/orders/search/useOrderSearch.ts') for(const token of ['appliedSearch','currentBulkFilter','appliedSearch.value={...search}'])if(!orderSearchVm.includes(token))failures.push(`OMS-ORD-001 applied-filter safety missing ${token}`) const orderList=read('apps/web/src/modules/oms/orders/search/OrderListPage.vue') for(const token of ['allow-all-filtered-selection','selection-state','total-count','selection-state-changed'])if(!orderList.includes(token))failures.push(`OMS-ORD-001 all-filtered adoption missing ${token}`) const searchRequest=read('backend/Modules/OMS/Orders/Search/Request.cs') const searchHandler=read('backend/Modules/OMS/Orders/Search/Handler.cs') if(!searchRequest.includes('bool ExceptionOnly'))failures.push('SearchOrdersRequest must carry ExceptionOnly') if(!searchHandler.includes('@ExceptionOnly = false or o.exception_count > 0'))failures.push('SearchOrdersHandler must enforce ExceptionOnly') const ship=read('backend/Modules/OMS/Orders/Ship/Endpoint.cs') for(const token of ['ShipOrdersFilter','mode is not ("ids" or "filter")','oms_order_search_projection','ExcludedIds','target as materialized','accepted as materialized','audit_insert','outbox_insert'])if(!ship.includes(token))failures.push(`ship server-side bulk contract missing ${token}`) const catalog=read('packages/kbx-ui/src/catalog/componentCatalog.ts') for(const token of ["id:'fast-entry'","id:'all-filtered'"])if(!catalog.includes(token))failures.push(`catalog missing ${token}`) const tests=read('tests/unit/kbx-grid-productivity.contract.spec.ts')+read('tests/e2e/kbx-grid-fast-entry.spec.ts') for(const token of ['normalizes Excel-like pasted values','does not expand all-filtered selection','server-side whole-result selection'])if(!tests.includes(token))failures.push(`grid productivity test evidence missing ${token}`) if(failures.length){console.error('KBX grid productivity validation failed:\n- '+failures.join('\n- '));process.exit(1)} console.log('Grid productivity PASS: Fast Entry, paste normalization, Fill Down, error navigation, drill-down/context hooks, all-filtered server bulk selection.')