chore(mgr): удалить мёртвые ExtJS-ассеты и legacy-конфиги окон (#521) - #602
Merged
Conversation
Phase 3 groundwork for #521: delete ExtJS files that no live code consumes, without touching the resource shell itself (that stays for #527/#528). Verified dead before removing: - `misc/ms3.combo.js` (1008 LOC) registers 25 xtypes. Vue `DynamicField` covers the 8 that PHP actually emits (`ms3-combo-select|options`, `ms3-key-value`, `ms3-repeater` are the only ones in src/). None of the remaining xtypes appear in product/*.js, category/*.js, PHP, migrations or Vue — the only external reference was `ms3-field-search`, used solely by `default.grid.js`, which is itself dead. - `misc/default.grid.js` / `misc/default.window.js` register `ms3-grid-default` / `ms3-window-default` — zero consumers anywhere. - `misc/ms3.utils.js` was referenced only by `default.grid.js`. - `misc/sortable/sortable.min.js` was used only inside `ms3.combo.js`. - `misc/strftime-min-1.3.js` had no code consumer at all, only the loader. - `config/mgr/settings/**/*.json` (5 window/grid configs) are read by nothing in PHP or Vue — leftovers from the Ext settings windows that moved to Vue in #523. Also drops the now-pointless addJavascript() calls from the four resource controllers (product/category create+update) and refreshes a stale docblock in Product\GetList that pointed at the removed ms3.combo.Product. Still loaded and intentionally kept: `minishop3.js` (the `ms3.*` Ext namespace used by product/category panels) and `misc/ms3.manager.js` (QuickCreateResource override for creating products/categories from the resource tree).
This was referenced Aug 18, 2026
Ibochkarev
added a commit
to Ibochkarev/MiniShop3
that referenced
this pull request
Aug 21, 2026
* fix(order): cart-only base for percentage payment commission (#460) The percentage payment commission used two different bases: storefront charged % of cart_cost, while the manager recalculate used cart_cost + delivery_cost — so the same order got a different total at checkout vs after a manager recalc (and a negative/discounted delivery leaked into the card fee). - New OrderService::paymentCommissionBase(cartCost) = cart-only (MS2-compatible, delivery excluded). - ManagerOrderCostRecalculator now uses it (cart-only) instead of cart+delivery; OrderCostCalculator (storefront) routes through the same helper (behavior preserved — it was already cart-only). - Adds PaymentCommissionBaseTest. Aligns storefront and manager on one base. Finalize is aligned separately (#448). Closes #372. * fix(order): align finalize cost with ManagerOrderCostRecalculator (#448) * fix(order): align finalize cost with ManagerOrderCostRecalculator Extract calculateBreakdown() from manager recalculate and use it when finalizing draft orders so free_delivery_amount, percent delivery, and payment commission match the recalculate-cost path. Closes #373 * refactor(order): extract OrderPersistedCostRules for shared cost math Move default delivery/payment formulas to a pure helper used by ManagerOrderCostRecalculator and smoke tests. Clarify recalculator docblock for manager recalculate and draft finalize paths. * fix(order): block finalize when cost breakdown has warnings Return error from calculateCosts when ManagerOrderCostRecalculator reports degraded AUTO breakdown so draft orders are not finalized with potentially wrong totals. Surface warnings in Vue with recalc hints. * test(order): exercise calculateBreakdown in cost rules smoke test Add xPDO/modX stubs and integration cases for ManagerOrderCostRecalculator including free delivery, cart+delivery payment fee, and custom handler warnings. * test(order): use msDelivery double in finalize integration test ManagerOrderCostRecalculator::isSimpleDelivery requires msDelivery. * test(order): stub msDelivery via PHPUnit createStub Avoid msDelivery(xPDO) constructor in finalize integration test. * test(order): stub msPayment in finalize integration modX double Fix wrong anonymous msDelivery stand-in for msPayment lookups. * test(order): align breakdown expectations to cart-only payment base calculateBreakdown() derives the payment commission from the cart-only base (OrderService::paymentCommissionBase, #460): 3% of 1000 = 30.0 and integrated total 1100.0 — not the former cart+delivery 32.1 / 1102.1. Neutralize the base-agnostic helper's assertion label. * test(order): cart-only payment commission in recalculator rules (#460) After rebase on beta, ManagerOrderCostRecalculatorRulesTest still expected 3% on cart+delivery (32.1). Align expectations with OrderService cart-only base. --------- Co-authored-by: biz87 <[email protected]> * fix(order): revalidate fixed status after plugin status mutation (#464) Extract validateStatusTransition() and run it again after msOnBeforeChangeOrderStatus may change the target status id. * fix(order): block save while cost recalculation is in flight (#461) Prevent lost-update when Save PUT races with recalculate-cost by disabling the save action in UI and adding symmetric in-flight guards in OrderView. * fix: reject incompatible delivery/payment pairs on order submit (#459) Centralize msDeliveryMember checks in DeliveryService and enforce them on web submit, checkout field updates, manager create/update, and finalize. Closes #374 * fix: payment_link в письме о новом заказе (#458) * fix: fill payment_link in status change email notifications StatusChangedNotification now resolves the online payment URL once per notification via PaymentLinkResolver and passes it to email templates. Removes the broken unused OrderStatusService::getPaymentLink helper. Closes #407 * refactor: unify payment link gating and add resolve smoke test Share payStatus parsing and eligibility rules between StatusChangedNotification and ms3_get_order via PaymentLinkResolver. Add PaymentLinkResolverResolveTest with stub handler (no MODX bootstrap). * fix(ci): drop loadCustomClasses, obsolete payment-link test, baseline * chore(deps): vueManager security bumps (js-yaml, fast-uri, happy-dom) (#506) Combine Dependabot #494 #500 #501: happy-dom 20.x, js-yaml 4.3.1, fast-uri 3.1.5. * Удалить deprecated pass-through OrderService/Customer (#489) * refactor: remove deprecated OrderService and Customer pass-throughs Drop handleOrderSave/removeOrder and Customer::getId wrappers after confirming zero in-repo callers; core uses msOrder::save/remove and Customer::getOrCreate directly. Closes #357 * test(regression): convert deprecated passthrough test to PHPUnit Move tests/DeprecatedPassThroughRemovedTest.php into tests/Unit/Regression/ as a PHPUnit class and narrow assertions to method_exists + ReflectionClass::hasMethod on OrderService and Customer, dropping the full-tree grep that was prone to false positives. * fix(mgr): stop loading missing vue-dist/main.min.css on product update (#504) Vite never emits main.min.css; product-tabs already registers the needed CSS/JS. Drop unused main.min.js too. Closes #503. * fix(currency): repair mojibake "?" default for ms3_currency_symbol (#498) Use ASCII-safe U+20BD in transport defaults, normalize "?" at read time for ms3Config/Format, and migrate existing broken system settings. * fix(vue): unwrap empty object/data in request.js connector responses (#466) Empty {} or [] payloads were falling through to the full success envelope, so grid callers expecting .results saw Invalid response without a toast. * fix(vue): prevent stale grid responses on fast filter/page changes (#468) Add useStaleRequestGuard with AbortController, sequence checks, and runGuarded(); wire OrdersGrid and CategoryProductsGrid list loaders. * fix(cart): смена опций в корзине через Web API и storefront (#502) * fix(web-api): wire cart/changeOption end-to-end on the storefront Selects with ms3_action=cart/changeOption now hit POST /api/v1/cart/change-option, re-render SSR cart blocks safely when multiple msCart roots exist, and emit option-change events on merge. * chore: remove changelog entries from cart changeOption PR Changelog is maintained at release time, not per-PR. * fix(import): show toast and step errors for fields/preview failures (#453) Closes #387 by surfacing loadAvailableFields and previewFile errors in UI, adding Toast host, clearing preview state on failure, and unifying import error helpers. * test(integration): finalize required fields + cart SQLite draft (#495) * test(integration): deepen finalize validation and cart SQLite draft store Cover delivery required_fields without skip_validation, and persist cart add/change/remove through an in-memory SQLite order-product store. * test(finalize): stub msDelivery and delivery service in integration harness OrderFinalizeServiceTest doubles used xPDOSimpleObject for delivery and omitted ms3_delivery_service, which broke after #448/#459 type checks and pair validation on finalize. * test(integration): MySQL CI + AuthManager lifecycle (#496) * test(integration): MySQL CI service and AuthManager lifecycle Add MySQL 8 service to PHP CI for @group mysql tests, cover AuthManager authenticate/session/logout/lockout against PDO stores, and OptionSync on MySQL. * fix(test): ignore option key order in MySQL OptionSync assert MySQL may return tags before color; compare after ksort. * fix(test): drop typed $services/$lexicon on modX doubles PHP 8.2+ rejects typed property redeclarations when ModxStub declares untyped $lexicon/$services. Anonymous test doubles must assign without redeclaring the property type. * chore(api): remove dead field-config override endpoints (#383) * chore(api): remove dead field-config override endpoints (#347) Drop no-op removeFieldOverride/saveFieldsConfig shims and the DELETE page-fields route after ms3_field_config_overrides was removed. * fix(test): drop DELETE page-fields override from ConfigRoutePermissionsTest Route was removed in #347; assert it is absent and keep remaining page-fields ACL checks. * refactor(mgr-api): extract extra-fields CRUD into ExtraFieldsController (#491) Move inline route closures to a Manager controller with DI-backed ExtraFieldsService; routes only dispatch. Adds getField() on the service. * refactor(api): единый Response envelope на HTTP boundary (#505) * refactor(api): unify HTTP Response envelope for mgr import/gallery Map processor bridges through Response::fromProcessor so connector Index stops nesting raw getResponse() payloads, and document domain vs HTTP contracts for contributors (#341). * fix(api): avoid MODX_CORE_PATH in RunsMs3Processors for PHPStan Resolve processors_path via dirname from the trait, and apply PSR spacing after function keywords in manager routes. * feat(api): лексикон для сообщений CategoryProductsController (#467) * feat(api): localize CategoryProductsController API messages Replace hardcoded English error/success strings with minishop3:default lexicon keys so manager toasts follow MODX cultureKey (ru/en). * fix(test): stub modX lexicon for CategoryProductsController smoke tests Constructor now calls lexicon->load(); ModxStub lacked the property, which broke CategoryProductsControllerScopeTest. * fix(products): дополнительные категории в msProducts и гриде категории (#482) * fix(products): unify category scope for msProducts and admin grid Extract CategoryProductScopeService so msProducts and the category products grid include msCategoryMember links, with regression tests. * fix(mgr): complete category grid scope for additional categories Add testable admin WHERE builder, clarify drag-sort for direct children only, and close #480 alongside #481 in the same PR. * fix(stan): align msProducts scope DI with PHPStan baseline Resolve CategoryProductScopeService from the container only and bump the ms3_products \$services ignore count after the #481 wiring. * refactor(products): drop dead isProductInCategory() method CategoryProductScopeService::isProductInCategory() has no callers in the codebase (verified by grep across src/, tests/, vueManager/). Remove it to keep the scope surface minimal (#481 review). * fix(stan): use CategoryProductScopeService for inline-edit scope check ProductDataController no longer calls removed CategoryProductsListService::isProductInCategoryScope(); delegate to findInCategory() which includes msCategoryMember links. * feat(mgr-api): document-level ACL для category products (#473) * feat(mgr-api): document-level ACL for category products Add checkPolicy view/save/publish/delete on CategoryProductsController endpoints to complement global msproduct_* permissions (#378). * fix(tests): stub document ACL for category products scope smoke CategoryProductsControllerScopeTest failed after #445 because sort requires a category with checkPolicy(view) and products with save. * fix(mgr-api): correct ACL list total and bulk 403 responses Over-fetch visible rows for paginated grids, count ACL-visible total, batch-load products for view filter, and return 403 when bulk actions are denied solely by document policy. * feat(utils): add EventGate for returnedValues contract (#456) Centralize returnedValues parsing and cancellation helpers so ImportCSV and Utils::invokeEvent share one protocol (#219). Removes duplicate private helpers from ImportCSV. * refactor(import): декомпозиция ImportCSV, EventGate и ProductImportService (#499) * refactor(import): decompose ImportCSV into Product/Import services Split the 999-line ImportCSV monolith into focused collaborators (reader, row mapper/processor, upserter, options, gallery, events) with a thin facade under 200 LOC. Preserves CSV contract, returnedValues events, and OptionSync path for option.* columns. Refs #344 * fix(import): address review findings for ImportCSV split - Add ImportCsvPathGuard for CSV and gallery path traversal checks - Re-validate CSV path after msOnBeforeImport returnedValues - Remove unused encoding passthroughs from ImportCSV facade - Extend unit tests for path guard, missing column mapping Refs #344 * fix(import): resolve PHPStan findings in ImportCsv split Use print_r(..., true), modX path options instead of MODX_* constants, string select() for xPDO query, modResource return annotation, and widen EventBridge array types for list-shaped gallery payloads. Refs #344 * feat(events): add EventGate for returnedValues contract (#358) Centralize returnedValues parsing in Utils\EventGate and delegate Utils::invokeEvent through it. Migrate ImportCSV, notifications, ProductDataService and ms3_products; remove ImportCsvEventBridge. * refactor(events): address EventGate review findings Add EventGate::invokeRaw for direct invokeEvent call sites, extract invokeProductModifier in ProductDataService, migrate ms3_products, and move EventGate tests to PHPUnit under tests/Unit/Utils. * refactor(import): move ProductImportService to Services layer (#368) Register ms3_product_import in ServiceRegistry; processors and scheduler task use DI. Utils\ImportCSV remains a deprecated BC wrapper. * test(import): avoid deprecated ImportCSV in unit test Check BC wrapper via file path instead of loading deprecated class. * refactor(di): OptionSync/Loader + ManagerOrderCostRecalculator in ServiceRegistry (#477) * refactor(di): register option loader/sync and manager cost recalculator Wire OptionSyncService, OptionLoaderService, and ManagerOrderCostRecalculator through ServiceRegistry so ms3.services.php overrides work. OptionService receives loader/sync from the container; OrdersController resolves the cost recalculator via DI. Add ServiceRegistryDiTest smoke coverage. Closes #363 * refactor(di): inject OptionCategoryService into OptionService Address #363 review findings: - OptionService now receives OptionCategoryService via its constructor instead of instantiating it internally; ServiceRegistry resolves ms3_category_option_service from DI for the ms3_option_service factory - Extract ServiceRegistry factory maps (CONTROLLERS_WITH_MS3_ONLY, SERVICES_WITH_MODX_AND_MS3, SERVICES_WITH_DEPENDENCIES) and a new SERVICE_DEPENDENCIES map into public constants so they are inspectable - ServiceRegistryDiTest now asserts behaviorally that every factory-map key and every declared dependency is a registered service key, and that ms3_option_service depends on ms3_category_option_service * Унификация расчёта стоимости заказа: checkout ↔ manager (#476) * refactor(order): unify checkout and manager cost formulas Extract OrderCostEngine for shared delivery/payment math, align web payment commission base with manager (cart + delivery), and register manager recalculator in DI. * test(order): delivery/payment cost edge cases for unified engine Add unit tests in OrderCostEngineTest for the math edge cases called out in #366 review: - Delivery free-threshold: cart at/above free_delivery_amount → 0; just below → weight_price × weight + price; free_delivery_amount = 0 disables it - Percent delivery price applied to cart - Payment percent surcharge base = cart + delivery (#372): 3% of 1500 = 45, total 1545; fixed and empty-price surcharges * test(order): align cost engine tests with cart-only commission base (#460) OrderCostEngine delegates to OrderService::paymentCommissionBase(); update OrderCostEngineTest and PaymentCommissionBaseTest for MS2 cart-only parity after rebase on beta. * perf(orders): conditional Address JOIN and separate stats endpoint (#469) Skip Address JOIN when grid/filters/search do not need it; move stats off the list hot path to GET /api/mgr/orders/stats with include_stats opt-in. * feat(mgr-api): category-scoped PUT для inline-edit product data (#474) * feat(mgr-api): category-scoped PUT for grid inline-edit product data Add PUT /categories/{id}/products/{productId}/data with URL-bound scope check; switch CategoryProductsGrid to the new route (#455). * fix(mgr-api): document ACL + scope policy for inline-edit product data Apply CategoryProductDocumentPolicy save check on the updateProductData inline-edit path (#473 pattern) and resolve scope via CategoryProductScopePolicy through findInCategory() instead of the separate isProductInCategoryScope() bool-only getObject round-trip. - Import CategoryProductDocumentPolicy (cherry-picked from #473) - Refactor CategoryProductScopeService::findInCategory nested branch to delegate to CategoryProductScopePolicy::isParentInScope - Replace isProductInCategoryScope() call in updateProductData with scopeService()->findInCategory() (single lookup, yields product for ACL) - Add 403 save-policy guard + logDocumentPolicyDenied helper - Tests: CategoryProductDocumentPolicyTest + ACL denial / out-of-scope cases in CategoryProductsControllerScopeTest; stubs support per-product policies and lexicon * test(vue): expect category-scoped inline-edit PUT URL Match useCategoryProductsInlineEdit save path after #455 API change. * fix(mgr-api): accept stubs in logDocumentPolicyDenied Smoke CategoryProductsControllerScopeTest passes StubMsProduct; a msProduct type hint fataled under PHP 8 after the #473 rebase merge. * fix(mgr-api): simplify logDocumentPolicyDenied PHPDoc for PHPStan Drop object-shape syntax that phpDoc.parseError rejects. * feat(order): EmptyOrder events and Order::set validation errors (#393) * feat(order): wire EmptyOrder/GetOrderCost events and set errors (#343) Fire registered msOnBeforeEmptyOrder/msOnEmptyOrder on draft clean and msOnBeforeGetOrderCost/msOnGetOrderCost on getTotalCost. Order::set aggregates per-field add() failures. Draft create stays on SaveOrder; recalculate stays an internal write without inventing new event names. * test(order): smoke guards for EmptyOrder hooks and Order::set errors Cover #343 review asks: set() aggregates add() failures, clean() wires msOnBeforeEmptyOrder/msOnEmptyOrder, and keep GetOrderCost wiring check already present on beta via OrderCostCalculator. * fix(auth): ротация API-токена против token fixation (#516) * test(auth): lock API token rotation against fixation (#412) Runtime rotation already lands via establishCustomerSession; add smoke guards so Login/Register/verify/checkout keep minting instead of rebinding. * test(auth): fold #412 rotation guards into existing smokes Drop the parallel ApiTokenRotationPolicyTest file. Scope mint/revoke/ session_regenerate asserts to establishCustomerSession, and ban token rebind on Login/Register/verify/checkout call sites. * docs: clarify Controllers HTTP vs domain facade boundary (#511) Document layer roles and DI keys in readme.md, and mark Cart/Order/Customer as domain facades (not FastRoute controllers) so contributors stop mixing HTTP and MS2-style facade code under Controllers/. * refactor(grid): разрез GridConfigService (Phase A #365) (#513) * refactor(grid): split GridConfigService into repository and collaborators Phase A of #365: persistence, column-type validators, and relation JOIN grouping move out of the god-service so grid column work stays reviewable. Empty keep-list on save no longer treats missing names as wipe-all. * fix(grid): assign collection before @var for PHPStan PHPStan rejects @var on a return expression where $fields does not exist. * refactor(options): разрез OptionLoaderService (Phase B #365) (#514) * refactor(options): split OptionLoaderService into collaborators Phase B of #365: product value loading, admin fields, and caption overlays become separate classes; batch prefetch stays on the product loader. Facade keeps the public API used by OptionService and mgr. * fix(options): satisfy PHPStan on OptionLoader collaborators Cast option keys to string for by-ref array shape, and call select() with strings instead of an array that xPDO stubs reject. * refactor(product): разрез ProductDataService (Phase C #365) (#515) * refactor(product): split ProductDataService into save collaborators Phase C of #365: categories/options/links writers, modifier hooks, repeater support, and removal helper leave ProductDataService as a facade under the LOC budget while preserving msProductData contracts. * fix(stan): satisfy PHPStan for product save collaborators Cast product ids, resolve Product via getOne, and isolate modX eventData access behind a phpstan-ignore for the new ProductModifierHooks / writer / removal helpers. * feat(import): support msExtraField columns in product CSV import (#510) Merge active ExtraFields into the import field list and persist them via Product Create/Update after loadMap(), so Object Extension columns are no longer silently dropped from CSV mapping and saves. * feat(gallery): выбор главного превью без смены порядка (#512) * feat(gallery): allow choosing product preview without reordering Store preview_file_id on msProductData so managers can mark any gallery image as the main thumb while keeping position order for the gallery UI. * fix(gallery): satisfy PHPStan for preview resolve types * refactor(mgr-api): shared reference CRUD for deliveries and payments (#509) Extract mirrored Manager API CRUD/M2M into Ms3ReferenceCrudService so fixes land once; keep deliveries-active whitelist and validation_rules delivery-only. Treat limit=0 as all rows for PaymentsGrid. * feat(extra-fields): add ms3-key-value field type (#323) * feat(extra-fields): add ms3-key-value field type Introduce configurable key-value extra fields with fixed and free modes, backend validation via KeyValueFieldService, and Vue editor components for manager forms and extra field definitions. Closes #300 * fix(extra-fields): harden ms3-key-value after issue-to-pr review Align Vue defaults and product/order hydration with the repeater pattern, stabilize free-mode editing, add KeyValueFieldService smoke tests, and document the type in the package changelog. * chore: drop key-value entry from package changelog Changelog notes for ms3-key-value belong at release time, not in this PR. * fix(extra-fields): address key-value review findings Surface prepareObject validation errors with lexicon messages, warn on duplicate free-mode keys, document strip-first unknown-key handling, and harden numeric casting / config encoding. * chore(ci): retrigger checks after integration test fixes * test(product): stub key-value fields in ProductDataService harness Override getProductKeyValueFields in integration test so updateProductData tests do not require ms3_key_value_field in modX services container. * refactor(cart-customer): thin Cart/Customer facades via Services managers (#406) * refactor(cart-customer): extract thin facades into Services managers Move cart mutations and customer field/order resolution out of Controllers facades so they match the Order + managers pattern without breaking web API. * fix: resolve PHPStan issues in cart/customer services Drop redundant is_numeric on int cart id; use getOrder() instead of magic order property. * test(cart): init CartMutationHandler in HarnessCart Cart facade delegates mutations; integration harness must wire the handler. * test(auth): point checkout auto-login smoke at CustomerOrderResolver Facade extraction moved createFromOrderData out of Customer; keep the #412 rotation guard on the resolver that now owns auto-login. * chore: track CodeGraph .gitignore for local index artifacts Ignore the SQLite DB, daemon pid/socket, and logs while keeping the directory’s ignore rules in the repo. * refactor(mgr-api): ModelFieldsController → ModelField services (#517) * refactor(mgr-api): extract ModelField services from controller Move field/section CRUD and combo resolution into ModelFieldService and ModelFieldSectionService so ModelFieldsController stays a thin HTTP layer. * refactor(model-field): keep HTTP status mapping in the controller Services return domain success/error codes; ModelFieldsController maps them to Response status so HTTP stays at the API boundary. * fix(di): import OrderDraftManager and ProductImportService for IDE resolve * fix(grid): resolve relation fields in category products grid (#330) Category products list now JOINs related tables for relation-type grid columns (e.g. vendor address via vendor_id on msProductData). Adds GridRelationColumnResolver, SQL identifier validation, and reserved field name checks. Closes #328. * feat: вкладка «Категории» товара на Vue (#113) (#479) * feat: migrate product Categories tab from ExtJS to Vue Replace ms3-tree-categories with a Vue tab backed by a reusable ResourceCategoryTree widget and REST tree endpoint. Closes #113. * fix: type ProductCategoryTreeService with MODX\Revolution\modX Satisfy PHPStan for constructor/property typing and select() column aliases. * fix(mgr-api): honor client category selection on tree refresh When the Vue tree sends categories, checked state follows the client allowlist instead of stale msCategoryMember rows from the database. * fix(mgr-api): address PR #479 review findings - Drop invalid addCss for ResourceCategoryTree.min.css from product update and settings controllers; the widget styles ship inside the product-tabs and options CSS bundles, so the dedicated file 404'd. - Register ProductCategoryTreeService as ms3_product_category_tree in ServiceRegistry and inject it via the container in ProductDataController::getCategoriesTree instead of `new`. - Extract MiniShop3\Utils\IntArrayDecoder and route both BaseApiController::decodeIntArray and OptionsController::decodeIntArray through it, removing the duplicated ad-hoc implementations. The shared decoder deduplicates and keeps only positive ids. - Extract MiniShop3\Utils\ResourceCategoryTreeQueryTrait with the category/container class-key sets and SQL helpers, used by both ProductCategoryTreeService and OptionsController::getTree. * fix(stan): remove redundant array_values in IntArrayDecoder PHPStan: $out is already a list built via sequential append. * feat(web-api): implement public product catalog endpoints (#333) Closes #332 * feat(web-api): add customer order list and get endpoints (#425) Headless cabinet needs history alongside cancel; scope by customer_id and omit token/properties from public DTOs. * Заменить abandoned rakit/validation (#486) * refactor: replace abandoned rakit/validation with ValidationService Remove unmaintained rakit/validation and route customer/order/profile validation through a native pipe-rule adapter registered in DI. Closes #342 * fix(validation): align digits and regex rules with Rakit parity digits/digits_between now reject non-digit characters, and regex params are no longer split on commas inside the pattern. * refactor(validation): use DI for ValidationService in call sites Replace ValidationServiceLocator static calls with ms3_validation_service DI resolution via protected getValidationService() helpers in Customer, CustomerProfileController and OrderFieldManager. Add parity unit tests for the digits_between rule. * perf(vueManager): parallelize option field values in OrderView (#447) Replace sequential await in initOptionsFromProduct loop with Promise.all to reduce UI latency when editing order products with many option fields. Closes #359 * refactor(vue-manager): extract thin list/config/crud/sort composables (#397) * refactor(vue-manager): extract thin list/config/crud/sort composables Pull shared mgr grid scaffolding into focused composables without a schema-driven mega-grid. Migrate pilot and twin grids; AbortController+seq in useResourceList addresses stale list races (#385 foundation). * refactor(vue-manager): wire Vendors to shared formatters, drop dead searchQuery Move normalizeImagePath into displayFormatters; bind formatValue in Vendors; remove unused Deliveries/Payments searchQuery; stabilize formatValue number test. * test(vue-manager): cover useResourceList race; share normalizeImagePath Extract createResourceList for Node-testable AbortController/seq races and reuse displayFormatters.normalizeImagePath in FileBrowser. * test(vue): migrate node:test suites to vitest Vitest CI runs vitest run; node:test imports produced empty suites. * GridConfigService: request-scoped memoize getGridConfig (#488) * perf: memoize GridConfigService::getGridConfig per request Cache grid config by gridKey and includeHidden on the service instance, and invalidate entries when grid config is mutated in the same request. Closes #352 * docs(grid-config): document request-scoped singleton invariant Expand the gridConfigCache docblock to state why the memo is safe only under the DI singleton (one instance per request, no cross-request persistence) and that all four mutation methods invalidate via invalidateGridConfigCache(). * refactor(grid-config): route mutations through beginGridMutation helper Single entry point for cache invalidation before msGridField writes. * test(grid-config): adapt memo tests to facade repository path Drop typed $lexicon on GridConfigModxStub (conflicts with untyped modX stub) and expect two getCollection calls after empty saveGridConfig, since findNonSystemNotIn([]) short-circuits without a DB scan. * feat(settings): consolidate dual API combo lists, drop member processors (#490) * feat(settings): consolidate combo lists and drop duplicate member processors Share SettingsComboListService between ExtJS combo GetList and Manager REST dropdowns; remove unused Delivery↔Payment membership processors that duplicated Vue REST with hardcoded TODOs. * style(routes): PSR-12 space after function in payments-active route * fix(test): allow payments-active dropdown LOC in reference CRUD smoke Align PaymentsController thinness gate with Deliveries (<200) after #346 adds getActiveDropdown, and assert the new route method exists. * Распил OrderView.vue: composable и диалоги (#478) * refactor(vue): split OrderView into composable and dialog components Extract order screen logic to useOrderView and product/customer dialogs so OrderView.vue stays under the 1000 LOC target without changing UX. Closes #339 * refactor(vue): split useOrderView into domain composables (#339) Break the 1753-line useOrderView.js into focused composables so no file exceeds ~400 lines. The thin orchestrator wires shared refs (order, saving, editingProduct) and core computeds, delegating to: - useOrderLoad: order/refs/fields/extra-fields loading + empty-order init - useOrderSave: save / finalize / create order actions - useOrderProducts: edit / add / delete product dialog state - useOrderProductOptions: options table/json editing - useOrderCostRecalc: cost recalculation + shipping/payment baseline - useOrderCustomer: customer search + duplicate-customer dialog - useOrderPluginTabs: built-in + MS3OrderTabsRegistry plugin tab registry Public orderContext contract and the return shape consumed by OrderView.vue are preserved unchanged. No behavior/API changes. * test(order): point save/recalc race smoke at domain composables OrderSaveRecalcRaceTest still grepped OrderView.vue after #339 moved in-flight guards into useOrderSave / useOrderCostRecalc. * fix(vue): hydrate key-value extra fields after OrderView split Keep parseStructuredExtraFieldValue on load so ms3-key-value survives the #339 composable extraction after rebase onto beta. * fix(migrations): inline ruble symbol in currency mojibake repair (#520) Phinx during transport install must not depend on Format::DEFAULT_CURRENCY_SYMBOL; the Format class may not expose that constant yet when the migration runs. Closes #519 * fix(di): wire OptionCategoryService into ms3_option_service (#535) Register ms3_option_category_service separately from CategoryOptionService so OptionService construction no longer TypeErrors. Connector catches Throwable to keep JSON for Vue (#531, #532). * chore(mgr): remove orphan Ext assets and add smoke gate (#522) (#530) Delete unused category.tree.js and utilities/import/panel.js; utilities Import tab already uses Vue. OrphanExtAssetsTest prevents re-introducing dead loads. * feat(order): публичный sessionless API программного создания заказов (#508) * feat(order): add sessionless ProgrammaticOrderService Extras and cron can create finalized msOrder without cart/session via ms3_programmatic_order, with unique idempotency_key and origin=integration events through the existing finalize cost/number/status pipeline. * fix(tests): clear PHP 8.2 dynamic-property deprecations in CI Declare Router group state and test stub properties; align lexicon() with modX. * fix(stan): drop redundant ?? on Router group state Properties are declared non-nullable; PHPStan rejected the null coalescing. * fix(test): align ProgrammaticOrderService harness with modX lexicon and RecordingMsOrder Match lexicon() signature to parent and assign product ids on first save so order->save() does not duplicate lines. * feat(product): Vue вкладка «Связи» и трекер ExtJS→Vue формы (#518) * feat(product): migrate Links tab to Vue and close ExtJS form tracker Move product links CRUD to ProductLinkService + Manager REST, drop Ext links grid/window, and document remaining shell ExtJS for #350. * fix(product): address review findings for Links Vue tab Enforce DELETE scope with lexicon errors, reject batch ids[], extract testable link-type rules, add ProductLinkServiceTest, and replace ProductTabs v-else-if chain with built-in component map. * fix(product): remove redundant null coalesce on getRequestData() PHPStan: getRequestData() always returns array; ?? [] was invalid (#518). * feat(mgr): mount orders and order pages without Ext wrappers (#533) Use Help-style tpl + plain ms3.config, drop Ext wrappers and waitForElement. Deep-link order_id via GET preserved (#526). * feat(mgr): Settings Vue entry without Ext tabs (#536) Replace Ext modx-tabs shell with a single settings Vite entry and Help-style controller mount so deep-links stay on #tab-* hashes. * feat(mgr): mount customers and notifications without Ext wrappers (#534) Use Help-style tpl + plain ms3.config; drop Ext wrappers and waitForElement (#525). * feat(mgr): Utilities Vue entry without Ext tabs (#537) Replace Ext utilities shell with a single Vite entry and Help-style mount so all six tabs keep localStorage state without modx-tabs. * refactor(orders): decompose OrdersController + ServiceRegistry factory map (#492) * refactor(orders): decompose Manager OrdersController and add ServiceRegistry factories Extract list/mutation/products/presenter services to shrink OrdersController below 1k LOC; register manager order services in DI. Replace ServiceRegistry switch/in_array wiring with explicit ServiceRegistryFactories map. * fix(orders): PHPStan — xPDOQuery::select expects string for pagetitle * fix(orders): keep delivery/payment pair checks after OrdersController split Port #459 validateDeliveryPaymentPair into ManagerOrderMutationService, register ms3_payment_link_resolver in ServiceRegistryFactories (#458), and point DeliveryPaymentAvailabilityTest at the mutation service. * fix(orders): align DI smoke with ServiceRegistryFactories Point ServiceRegistryDiTest at the extracted factory map and resolve ManagerOrderCostRecalculator only via ms3_manager_order_cost_recalculator. * fix(vue): изоляция confirm-диалогов категории через group — двойной confirm (#538) (#540) * fix(vue): scope category confirm dialogs by group to stop double-confirm (#538) Страница category/update монтирует два независимых Vue-приложения (category-products + category-options). PrimeVue вынесен в общий Import Map, поэтому ConfirmationEventBus — модульный singleton: один ungrouped confirm.require() ловился обоими ungrouped <ConfirmDialog> → два диалога сразу. Namespace обоих приложений через confirm-группу: - useActions/useSelection: опциональная опция confirmGroup (по умолчанию ungrouped) - ActionsColumn: проп confirmGroup, проброс в useActions - CategoryProductsGrid: group="category-products" + убран висячий useConfirm() - CategoryOptionsTab: group="category-options" на диалоге и в обоих require Остальные гриды не затронуты (confirmGroup opt-in, по умолчанию ungrouped). * refactor(vue): harden confirm-group against silent-fail typos (#538) Follow-up polish on the review: - extract the group literal into a single CONFIRM_GROUP const per component (CategoryProductsGrid, CategoryOptionsTab) — a typo in any of the 3 usage sites (dialog / composable / prop) would silently match no dialog - document why `group: confirmGroup || undefined` is load-bearing in useActions/useSelection: PrimeVue ConfirmDialog matches strictly (options.group === this.group; ungrouped => undefined), and confirmGroup defaults to null, so reducing it to `group: confirmGroup` would break every ungrouped grid silently. No behavior change. * fix(mgr): токен в ms3.config — гонка HTTP_MODAUTH на Ext-less Vue-страницах (#544) (#545) * fix(mgr): inject auth token into ms3.config to kill HTTP_MODAUTH race (#544) Ext-less Vue manager pages fire their first API requests on DOMContentLoaded, before the manager JS populates the MODx.siteId global that request.js used as HTTP_MODAUTH. Those early requests went out tokenless, so the connector rejected them (HTTP 200 body {success:false, message:"Доступ запрещён.", object:{code:401}}), surfacing as intermittent "Доступ запрещён" on orders filters/grid-config while the later list request succeeded (grid filled). #533/#534/#536/#537 exposed the race by dropping the waitForElement guard. Ship the token synchronously in the inline ms3.config block, before the Vue module runs: - manager.class.php: new addVueConfig() helper injects token = getUserToken($contextKey) - customers/notifications/order/orders/settings/utilities: use addVueConfig() - request.js getModAuthToken(): read ms3.config.token first, fall back to MODx.siteId Covers all 6 Ext-less Vue pages. No permission/session change. * test(mgr): assert addVueConfig contract + base token injection (#544) Moving the ms3.config emission into the base addVueConfig() helper took the `var ms3` literal out of the page controllers, so the *VueEntryTest smoke guards that grep each controller for it failed. Switch the required pattern to `addVueConfig`, and add a base-controller guard in CustomersNotificationsVueEntryTest asserting controllers/manager.class.php ships `var ms3` + getUserToken (the #544 token injection that must not regress). * fix(vue): stop infinite recursive loop in ResourceCategoryTree (#546) (#547) watch(() => props.modelValue) called ensureLockedChecked() unconditionally, which emits update:modelValue with a fresh array. The parent's v-model echoes it straight back into the prop, retriggering the watch — an infinite recursive loop (Vue: "Maximum recursive updates exceeded") that froze Settings→Options and any page using the tree. The single /api/mgr/options request showed as pending only because the frozen main thread never processed the (instant, 0.006s) reply. Emit back only when locked enforcement actually adds an ID the parent lacks, so the watch no longer echoes the value it just received. Verified in isolation: 1 emit instead of an infinite loop. * fix(vue): isolate Settings tab ConfirmDialogs with unique groups (#548) (#549) Visited Settings tabs and Options+Groups keep multiple ungrouped ConfirmDialogs on the shared PrimeVue bus, so delete shows two dialogs. * fix(vue): remove redundant VendorsGrid delete confirm (#551) (#552) VendorsGrid's ActionsColumn delete action was confirm:true, so useActions showed a confirm and then deleteVendor() showed a second one — a sequential double confirm independent of the shared-bus grouping fixed in #548/#549. Align it to confirm:false like the other settings grids; deleteVendor() remains the single grouped confirm. * chore(release): 1.13.0-beta1 — version bump + changelog (#560) Bump version to 1.13.0-beta1 (_build/config.inc.php version 1.13.0/beta1, MiniShop3.php). Add CHANGELOG.md (RU, detailed) and docs/changelog.txt (EN, Keep-a-Changelog) entries summarizing the 107 commits since v1.12.0-beta1: public web-API (catalog/customer/programmatic orders), Ext-less Vue manager migration, service decomposition, API security/ACL hardening, and the late-cycle Vue regression fixes (#538/#544/#546/#548/#551). * fix(schema): add missing idempotency_key field to msOrder schema.xml (#562) Migration 20260809140000_add_order_idempotency_key.php and the generated model map (src/Model/mysql/msOrder.php) both have the unique nullable varchar(128) idempotency_key column on ms3_orders, but schema.xml — the source xPDO regenerates the model from — never got it. Sibling fields added in the same release cycle (preview_file_id, key_value_config) were added to schema.xml correctly; this one was missed. Left as-is, a future model regeneration from schema.xml would silently drop the column from the map. * fix(mgr): declare ms3 via addVueConfig on help page (#554) Help assigned ms3.config without var ms3, which throws ReferenceError before the Vue module mounts. Reuse the same helper as other Vue mgr pages. * fix(vue): space category tree checkbox from its label (#557) * fix(vue): space category tree checkbox from its label PrimeVue Checkbox's box overflows the flex item, so row gap did not show. Put 0.5rem on the label that follows the checkbox. * fix(mgr): load ResourceCategoryTree CSS on product update Vite emits tree styles as a shared chunk. product-tabs.min.css does not include them, so the checkbox gap never reached the Categories tab. * fix(vue): reuse ms3_menu_remove for product link confirm header (#559) The title key ms3_menu_remove_title does not exist. The confirm body already uses manager lexicon; the same Remove string is enough. * fix(product): persist composite PK when creating product links (#561) xPDO fromArray() skips PK fields on msProductLink, so save() failed and the API returned ms3_err_unknown. Write link/master/slave via set() and return ms3_err_link_save / ms3_err_no_link instead. * fix(vue): изоляция PrimeVue Confirm/Toast через UI groups (#539) (#543) * fix(vue): isolate PrimeVue Confirm/Toast via UI groups (#539) Shared Import Map makes ConfirmationEventBus/ToastEventBus module singletons, so ungrouped dialogs/toasts on multi-app or non-lazy tab pages all fire together. Add provide/inject + useGroupedToast, group product gallery vs links confirms, and namespace category toast/confirm. * fix(vue): silence ESLint on ui-group smoke script Import node:process and use console.warn so lint:ci --max-warnings 0 passes for the #539 smoke check. * fix(vue): address UI group review — full toast facade, uiGroup rename Spread ToastService through useGroupedToast, rename confirmGroup to uiGroup (keep alias), document provide vs local constants, and tighten mount/smoke coverage from #543 review. * fix(web): локаль toast корзины через ctx страницы (#541) (#542) * fix(web): pass page context to Cart API for toast lexicon (#541) api.php always initialized MODX as web, so cart success messages used web cultureKey on Babel/multi-context pages. Send ms3Config.ctx on requests, switchContext safely, and wire plugin/registerFrontend to the real page context. * fix(web): harden page ctx switch and unify cart draft context (#541) Guard unknown ctx before switchContext (MODX nulls context), share CartDraftContext for Cart/Order when ms3_cart_context=1, and align tests with real failure modes from PR review. * style(web): drop useless try/catch and semicolons in ApiClient * fix(events): register msOnBeforeUpdateCustomer / msOnUpdateCustomer (#581) Both events are fired by MiniShop3\Processors\Customer\Update (MODX UpdateProcessor $beforeSaveEvent / $afterSaveEvent) but were missing from _build/elements/events.php, which is the only place build.php reads to package modEvent records. Unregistered events cannot be hooked by plugins through the standard UI. Found during the full event-registry audit against every invokeEvent call site (direct calls, EventGate::invokeRaw and processor lifecycle hooks). Docs for both events already landed in modx-pro/Docs#1012. * feat(web-api): публичный Category API (list / get / tree) (#578) * feat(web-api): add public category list/get/tree for headless Expose published msCategory catalog under /api/v1/category so Nuxt can build nav, category pages and breadcrumbs without Manager API. * fix(web-api): remove dead empty breadcrumbs branch for PHPStan After appending the current category id, $ids is never empty, so the identical.alwaysFalse check failed analysis. * refactor(web-api): extract CatalogQuery helpers for product/category Share limit/offset/sort/context/depth parsing so CategoryCatalogService does not depend on ProductCatalogService statics. * fix(web-api): harden category catalog context and tree loading Empty context now falls back instead of dropping context_key. Tree loads BFS parent+depth windows with a node cap; include_children uses list limit. * fix(web-api): hardening rate-limit, CORS wildcards, query tokens (#584) Ключ rate limit только по IP, CORS-паттерны через preg_quote и одну DNS-метку, session token не принимается из query string. * feat(web-api): единый контракт ошибок и HTTP-статусов (#583) * feat(web-api): единый контракт ошибок и HTTP-статусов Сохраняем envelope #341 и добавляем machine error_code, чтобы Nuxt получал стабильные 401/404/422/429 и field errors без getData()-потери status. * fix(web-api): убрать array-shape из PHPDoc return Response Intelephense читал `@return Response ['…']` как array и ругался на Response::error. * fix(web-api): убрать локальные success/error в CustomerEmailController Имя error() совпадало с Response::error и давало ложный Expected array в IDE. * fix(web-api): явный return type Response у фабрик и EmailController Убирает ложный Expected array у return Response::error в IDE. * fix(web-api): Order/Cart domain map через DomainMs2Response::fromDomain Убирает transformResponse в OrderController, из‑за которого IDE видела return array. * feat(web-api): публичные delivery/list и payment/list (#586) * feat(web-api): public delivery and payment discovery catalogs Add GET /api/v1/delivery|payment list/get with hard allowlists so headless checkouts can discover active methods without Manager API or leaking gateway properties. * fix(web-api): pass string columns to xPDOQuery::select PHPStan stubs type select() as string-only; use comma-separated column lists in CheckoutMemberMap. * perf(web-api): batch-load product Data on catalog list (#587) Avoid per-row getOne(Data) on product/list, drop useless COUNT DISTINCT for the 1:1 Data join, and skip selecting content when include_content is off. * feat(grid): filter category products by relation columns (#585) Apply filter_{field} LIKE on joined relation display fields and send the same prefix from the manager grid so UI filters match option-column behaviour. * feat(web-api): контракт customer auth для Nuxt SSR (me / refresh / Bearer bind) (#588) * feat(web-api): customer auth contract for Nuxt SSR (me / refresh / Bearer bind) Add GET /customer/me introspection, real POST /customer/token/refresh rotation, and validate Bearer before binding guest cart on login/register. * fix(web-api): drop unused \$params in customer route closures * test(web-api): align query-token smoke with TokenService resolver TokenMiddleware no longer owns resolveToken(); #576 checks target TokenService::resolveTokenFromRequest after the #571 auth rebase. * feat(web-api): расширенные фильтры product/list (#580) * feat(web-api): add validated filters to product/list for headless Support parents/nested, price/stock/vendor/flags, and options filters on the public catalog list without raw SQL, with 400 lexicon errors. * fix(web-api): keep option GROUP BY off count queries COUNT(DISTINCT) with GROUP BY returned per-row counts and broke total when options filters joined multi-value rows. Also harden empty context. * style(web-api): add trailing newline to ProductCatalogFilterApplier * fix(web-api): gate COUNT(DISTINCT) to option filter joins Keep the #575 1:1 COUNT(msProduct.id) path; use DISTINCT only when option filters join multi-value rows after rebase onto #587. * test(web-api): headless Nuxt journey suite через Router envelope (#595) * test(web-api): add headless Nuxt journey suite via Router envelope Catch storefront wire regressions (middleware, HTTP status, cart→login transfer) that domain-only tests miss. Closes the Phase A+B gate for #574. * fix(test): load Sqlite draft harness via bootstrap on Linux PSR-4 maps Support/ but files live in tests/support/; CI is case-sensitive and failed with trait not found (#595). * test(web-api): align journey suite with beta CORS and mint contracts After rebase onto #584/#583: use CorsConfig::isOriginAllowed for preflight smoke, and expect 500/internal mint failure instead of 401. * test(web-api): sync Journey token stubs with TokenMiddleware (#588) After rebase onto beta with customer auth contract, middleware calls syncSessionFromToken(); journey doubles must implement it. * feat(web-api): product/filters — facets для headless PLP (#597) * feat(web-api): product/filters facets for headless PLP Add public GET /api/v1/product/filters with soft-count price/option/vendor facets on the same storefront scope as product/list (#564 applier). * fix(web-api): satisfy PHPStan on product facets cache/ok flags Track SQL success via return tuples instead of a mutable queryFailed flag, and guard cacheManager without isset/?? on initialized modx. * feat(db): composite indexes on ms3_product_options for facets Add (key, product_id) and (product_id, key), drop redundant single-column indexes, and sync xPDO metaMap for #565 storefront facet queries. * chore(mgr): remove dead ExtJS assets and stale window configs (#521) (#602) Phase 3 groundwork for #521: delete ExtJS files that no live code consumes, without touching the resource shell itself (that stays for #527/#528). Verified dead before removing: - `misc/ms3.combo.js` (1008 LOC) registers 25 xtypes. Vue `DynamicField` covers the 8 that PHP actually emits (`ms3-combo-select|options`, `ms3-key-value`, `ms3-repeater` are the only ones in src/). None of the remaining xtypes appear in product/*.js, category/*.js, PHP, migrations or Vue — the only external reference was `ms3-field-search`, used solely by `default.grid.js`, which is itself dead. - `misc/default.grid.js` / `misc/default.window.js` register `ms3-grid-default` / `ms3-window-default` — zero consumers anywhere. - `misc/ms3.utils.js` was referenced only by `default.grid.js`. - `misc/sortable/sortable.min.js` was used only inside `ms3.combo.js`. - `misc/strftime-min-1.3.js` had no code consumer at all, only the loader. - `config/mgr/settings/**/*.json` (5 window/grid configs) are read by nothing in PHP or Vue — leftovers from the Ext settings windows that moved to Vue in #523. Also drops the now-pointless addJavascript() calls from the four resource controllers (product/category create+update) and refreshes a stale docblock in Product\GetList that pointed at the removed ms3.combo.Product. Still loaded and intentionally kept: `minishop3.js` (the `ms3.*` Ext namespace used by product/category panels) and `misc/ms3.manager.js` (QuickCreateResource override for creating products/categories from the resource tree). --------- Co-authored-by: biz87 <[email protected]> Co-authored-by: Cursor Agent <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Часть Phase 3 из #521, без миграции resource shell — формы товара и категории остаются на Ext, как есть (это #527/#528).
Удалено только то, что подтверждённо никем не используется. Итог: −2128 строк.
Что удалено и почему это безопасно
misc/ms3.combo.jsms3-combo-select,ms3-combo-options,ms3-key-value,ms3-repeater— все четыре рендерит VueDynamicField. Остальные не встречаются ни вproduct/*.js, ни вcategory/*.js, ни в PHP, ни в миграциях, ни во Vuemisc/default.grid.jsms3-grid-default— 0 потребителейmisc/default.window.jsms3-window-default— 0 потребителейmisc/ms3.utils.jsdefault.grid.jsmisc/sortable/sortable.min.jsms3.combo.jsmisc/strftime-min-1.3.jsconfig/mgr/settings/**/*.jsonЕдинственной внешней ссылкой на
ms3.combo.jsбыл xtypems3-field-search, и тот использовался только вdefault.grid.js, который сам мёртв.Что ещё изменено
addJavascript()этих файлов из четырёх resource-контроллеров (product/category create+update).Processors\Product\GetList, который ссылался на удалённыйms3.combo.Product(сам процессор оставлен — его могут звать сторонние клиенты по имени action).Что намеренно оставлено
minishop3.js— базовый namespacems3.*, на нём держатся Ext-панели товара и категории.misc/ms3.manager.js— overrideMODx.window.QuickCreateResource(быстрое создание товара/категории из дерева ресурсов), живая пользовательская функциональность.Проверка
PHPStan локально даёт 2 ошибки в
ConfigService.php— это известное расхождение локальной версии с CI-pinned, файл этим PR не затрагивается.