array( 'name' => 'Store Owner', 'description' => 'Runs daily store operations, finances, settings, and the team.', ), 'storesuit_staff' => array( 'name' => 'Store Staff', 'description' => 'Handles orders, products, customers, stock, and fulfilment without access to financial approvals or team settings.', ), 'storesuit_cashier' => array( 'name' => 'Cashier', 'description' => 'Sells at checkout and finds orders without access to costs, reports, customer profiles, or settings.', ), 'storesuit_warehouse' => array( 'name' => 'Warehouse & shipping', 'description' => 'Picks, packs and ships orders and prints their packing slips and labels, without seeing prices, totals or payments.', ), ); /** * The Warehouse & shipping preset (OD-7). It holds only the fulfilment permission (storesuit_fulfil) and wp-admin access. The * order desk asks storesuit_orders everywhere, so a member gets that permission only inside the requests of their work: the * order desk page, the fulfilment router actions (WAREHOUSE_ACTIONS), an order's official shipping panel (reading it, the * handover, preparing, quoting and, where the provider's own purchase switch is on, buying a label; never cancelling or * refunding one) and the print pages (pick list, packing slips without prices, labels). Every answer the router gives them is * read without prices, totals or payments through the warehouse response filter. Their other StoreRuler pages lead to the order desk's shipping * queue. An administrator switches the preset on and off in Team settings; it cannot be switched off while someone holds it. */ public const WAREHOUSE = 'storesuit_warehouse'; /** What this request grants a Warehouse & shipping member (warehouse_grant(); null until read). */ private static $warehouse_grant = null; private const WAREHOUSE_OPTION = 'storesuit_warehouse_role'; public const WAREHOUSE_ACTIONS = array( 'state', 'order', 'order_timeline', 'order_views_read', 'fulfil', 'order_advance', 'orders_flow_bulk', 'shipment_tracking', 'carrier_profiles_read' ); private const WAREHOUSE_SHIPPING = array( 'order', 'handover_read', 'handover_save', 'geliver_prepare', 'geliver_refresh', 'geliver_buy', 'sendcloud_sender', 'sendcloud_quote', 'sendcloud_home', 'sendcloud_refresh', 'sendcloud_buy', 'easypost_quote', 'easypost_refresh', 'easypost_buy', 'parcel2go_quote', 'parcel2go_dropoffs', 'parcel2go_dates', 'parcel2go_buy', 'parcel2go_pay', 'parcel2go_refresh' ); /** A label purchase asks the approval permission; a Warehouse & shipping member has it for one only while the provider's own switch lets StoreRuler buy. */ private const WAREHOUSE_BUYS = array( 'geliver_buy' => 'geliver', 'sendcloud_buy' => 'sendcloud', 'easypost_buy' => 'easypost', 'parcel2go_buy' => 'parcel2go', 'parcel2go_pay' => 'parcel2go', ); private const WAREHOUSE_PRINTS = array( 'storesuit_pick_list', 'storesuit_packing_slips', 'storesuit_shipping_labels', 'storesuit_official_label', 'storesuit_official_easypost_label', 'storesuit_official_parcel2go_label', 'storesuit_official_sendcloud_label', 'storesuit_sendcloud_label' ); private const WOO_SETTINGS_ACTIONS = array( 'woocommerce_update_api_key', 'woocommerce_tax_rates_save_changes', 'woocommerce_shipping_zones_save_changes', 'woocommerce_shipping_zone_add_method', 'woocommerce_shipping_zone_remove_method', 'woocommerce_shipping_zone_methods_save_changes', 'woocommerce_shipping_zone_methods_save_settings', 'woocommerce_shipping_classes_save_changes', 'woocommerce_shipping_providers_save_changes', 'woocommerce_toggle_gateway_enabled' ); private const WOO_REFUND_ACTIONS = array('woocommerce_refund_line_items', 'woocommerce_delete_refund'); private const WOO_ORDER_WRITE_ACTIONS = array( 'woocommerce_mark_order_status', 'woocommerce_add_order_item', 'woocommerce_add_order_fee', 'woocommerce_add_order_shipping', 'woocommerce_add_order_tax', 'woocommerce_add_coupon_discount', 'woocommerce_remove_order_coupon', 'woocommerce_remove_order_item', 'woocommerce_remove_order_tax', 'woocommerce_calc_line_taxes', 'woocommerce_save_order_items', 'woocommerce_order_add_meta', 'woocommerce_order_delete_meta' ); public static function hooks() : void { self::install(); } /** Pro-only methods live under premium-suffixed names; old callers keep their names (paid build only). */ public static function __callStatic( string $name, array $args ) { throw new \BadMethodCallException(esc_html( self::class . '::' . $name )); } public static function install() : void { if ( get_option( 'storesuit_roles_version' ) === self::VERSION ) { return; } foreach ( array('administrator', 'shop_manager') as $name ) { $role = get_role( $name ); if ( !$role ) { continue; } foreach ( array_keys( self::custom_caps( true, true, true, true, true, true ) ) as $cap ) { $role->add_cap( $cap ); } } update_option( 'storesuit_roles_version', self::VERSION, false ); } /** * Store Staff copied the shop manager's permissions, WooCommerce's reports permission with them: WooCommerce Analytics and its * home and dashboard sales figures showed the revenue (owner, 28 Oct 2026: staff never see the owner's numbers). install() * withholds these from the preset. With them (Denetim R0-3, 28 Oct 2026) the shop manager's WordPress powers outside the staff's * work: the user levels, deleting posts, pages, products and orders, the theme options, import and export, and the user list * (every customer's e-mail). staff_switch_caps() gives back what an area switch needs; deleting products and media are switches * of their own (staff_delete_caps()). */ public const STAFF_WITHHELD = array( 'view_woocommerce_reports', 'level_1', 'level_2', 'level_3', 'level_4', 'level_5', 'level_6', 'level_7', 'level_8', 'level_9', 'delete_posts', 'delete_others_posts', 'delete_published_posts', 'delete_private_posts', 'delete_pages', 'delete_others_pages', 'delete_published_pages', 'delete_private_pages', 'delete_products', 'delete_others_products', 'delete_published_products', 'delete_private_products', 'delete_shop_orders', 'delete_others_shop_orders', 'delete_published_shop_orders', 'delete_private_shop_orders', 'edit_theme_options', 'import', 'export', 'list_users' ); /** Whether the owner lets Store Staff set stock on hand directly (Team settings). */ public static function staff_stock_edit() : bool { return get_option( self::STAFF_STOCK_OPTION, 'no' ) === 'yes'; } private static function custom_caps( bool $orders, bool $fulfil, bool $finance, bool $approve, bool $team, bool $stock = false ) : array { return array_filter( array( 'storesuit_orders' => $orders, 'storesuit_fulfil' => $fulfil, 'storesuit_finance' => $finance, 'storesuit_approve' => $approve, self::MANAGE => $team, self::POS => $orders, self::STOCK_EDIT => $stock, self::REFUND => $finance && $approve, self::COLLECT => $finance, 'storesuit_prices' => $team, 'storesuit_content' => $team, 'storesuit_design' => $team, 'storesuit_settings' => $team, 'storesuit_menus' => $team, 'storesuit_delete_products' => $team, 'storesuit_delete_media' => $team, ) ); } }