docs: doc 11 blueprint WooCommerce (produtos/encomendas/clientes/cupoes/analytics/webhooks)

Fundamentado em leitura directa do WooCommerce 11.0.1 real instalado em
ecommerce.descomplicar.pt (nao no EMCP Tools Pro, cujo codigo Woo Integration
esta ausente do build Free). Cobre REST API wc/v3 + classes CRUD nativas,
capabilities reais por dominio, e tabelas de ability proposta para replica.
Exclui SEO/Yoast/RankMath por pedido.

Actualiza INDEX.md com a 12a entrada e totais revistos.
This commit is contained in:
Claude Code
2026-08-19 07:10:13 +01:00
parent 8ada367bd0
commit 2f98a527a8
2 changed files with 589 additions and 7 deletions
+580
View File
@@ -0,0 +1,580 @@
# 11 — WooCommerce: blueprint de abilities MCP (não é auditoria EMCP)
Fonte: leitura directa do código-fonte real do **WooCommerce 11.0.1**, instalado em
`ecommerce.descomplicar.pt` (`/home/ealmeida/ecommerce.descomplicar.pt/wp-content/plugins/woocommerce/`),
19-08-2026. Cruzado com `docs/00-ARQUITECTURA.md` (padrão `emcp_tools_register_ability()` /
`normalize_result()`) e `docs/10-MODULOS-E-INVENTARIO-PRO.md` (confirmação de que
`EMCP_Tools_Woo_Integration` está gated por `class_exists(...) && EMCP_Tools_Woo_Integration::woo_active()`,
sem module toggle dedicado — linha 470 desse documento). **Exclui por completo** SEO/Yoast/Rank
Math e licenciamento Freemius, que estão fora de âmbito.
## 0. Porque este documento é diferente dos outros 10
Os docs 01-10 desta série leem o código-fonte do plugin `emcp-tools` (a integração MCP em si) e
descrevem o que ele faz. **Este documento não pode fazer isso para WooCommerce**: a classe
`EMCP_Tools_Woo_Integration` é Pro-only e o seu ficheiro está fisicamente ausente da árvore Free
instalada (confirmado por tentativa de leitura nas sessões anteriores desta batch — só o
`class_exists()` gate e o comentário associado em `class-ability-registrar.php` são visíveis, ver
citação acima). Não há nada para auditar.
Em vez disso, este documento faz a pergunta inversa: **dado o que o WooCommerce em si expõe** —
REST API `wc/v3` (mais `wc/v4` emergente e `wc-analytics` para Store Analytics) e as classes CRUD
PHP nativas (`WC_Product`, `WC_Order`, `WC_Coupon`, `WC_Customer`) — **que grupo de abilities MCP
faria sentido construir do zero**, seguindo os padrões arquitecturais já documentados nesta série
(dispatcher de dois braços `<domínio>-read`/`<domínio>-write`, change ledger, `normalize_result`)?
Todas as tabelas de "ability proposta" abaixo são propostas próprias, fundamentadas em código real
lido nesta tarefa — nunca uma transcrição do que o EMCP Pro possa ou não fazer.
---
## 1. Panorama
O WooCommerce expõe **duas superfícies programáticas distintas**, nenhuma das quais é MCP:
1. **REST API `wc/v3`** — a API pública estável, versionada, autenticada por OAuth1/Basic Auth
(consumer key/secret) ou cookie+nonce em contexto admin. Routing centralizado em
`includes/rest-api/Server.php`, classe `Automattic\WooCommerce\RestApi\Server` (singleton,
hook `rest_api_init`). Confirmado por leitura integral do ficheiro: quatro namespaces legado
(`wc/v1`, `wc/v2`, `wc/v3`) mais um `wc-telemetry`, cada um com o seu próprio mapa
`rest_base => Controller_Class`. A v3 estende sempre a v2, que estende sempre a v1 — cadeia de
herança real (`WC_REST_Products_Controller extends WC_REST_Products_V2_Controller`, e assim
por diante), não reimplementação por versão.
2. **`wc/v4`** (namespace novo, `Server.php` linhas ~78-82) — **feature-gated** por
`Automattic\WooCommerce\Admin\Features\Features::is_enabled('rest-api-v4')`, ainda em
construção: só 12 recursos migrados (`fulfillments`, `products`, `customers`, `order-notes`,
`shipping-zones`, `orders`, `refunds`, `offline-payment-methods`, mais 6 controllers de
`settings-*`), implementados como classes PHP com namespace próprio
(`Automattic\WooCommerce\Internal\RestApi\Routes\V4\...`), não a família `WC_REST_*_Controller`
clássica. **Não é a superfície recomendada para uma réplica hoje** — instável e parcial; este
documento fundamenta-se na v3, estável e completa.
3. **`wc-analytics`** (Store Analytics) — namespace carregado só quando pedido
(`RestApiUtil::lazy_load_namespace('wc-analytics', ...)`, `src/Admin/API/Init.php`), com um
subconjunto que reexporta os controllers `wc/v3` sob outro namespace (`Orders extends
\WC_REST_Orders_Controller`) mais um conjunto próprio de controllers de relatório agregado
(`Automattic\WooCommerce\Admin\API\Reports\*`). Detalhado na secção 6.
4. **Classes CRUD PHP nativas** — `WC_Product` (+ `WC_Product_Variable`/`_Simple`/etc.),
`WC_Order` (+ `WC_Order_Item_Product`/`_Fee`/`_Shipping`/`_Coupon`), `WC_Coupon`, `WC_Customer`.
Todas herdam de `WC_Data` (padrão `get_prop()`/`set_prop()`/`save()`) com um **data store**
injectável — `WC_Product_Data_Store_CPT`, etc. — que faz a persistência real em `wp_posts`/
`wp_postmeta` (produtos/encomendas legado) ou nas tabelas HPOS próprias quando o
`custom_orders_table` está activo (`OrderUtil::custom_orders_table_usage_is_enabled()`,
referenciado em `WC_REST_Orders_Controller::prepare_objects_query()`).
**A relação entre as duas superfícies é directa, não paralela**: todos os controllers REST `wc/v3`
de CRUD (`WC_REST_CRUD_Controller`, base abstracta em
`includes/rest-api/Controllers/Version2/class-wc-rest-crud-controller.php`) implementam
`save_object()` construindo um objecto `WC_Product`/`WC_Order`/`WC_Coupon` via
`prepare_object_for_database()` e chamando `->save()` — o REST é uma camada fina de
serialização/validação/permissões por cima das mesmas classes CRUD. **Uma réplica MCP pode (e
deve) chamar as classes CRUD directamente** (`wc_get_product()`, `wc_get_products()`,
`wc_get_orders()`, `new WC_Coupon()`, etc.) em vez de fazer loopback HTTP à REST API — evita
autenticação redundante, é mais rápido, e dá acesso a `WP_Error`/`WC_Data_Exception` nativos em
vez de ter de reparsear respostas JSON.
**Nota de `normalize_result` (já documentada em `docs/00-ARQUITECTURA.md` §5)**: se uma réplica
optar por reutilizar `WP_REST_Response::get_data()` verbatim de rotas `wc/v3` em vez de chamar as
classes CRUD directamente, várias rotas devolvem array de topo em vez de objecto — confirmado
nesta tarefa em `WC_REST_CRUD_Controller::get_items()` (devolve `$objects`, um array indexado, não
um mapa), e nos controllers de relatório (`reports-products-totals`, etc., citados no doc00). Sem
um wrapper `{data: [...]}`, clientes MCP estritos (Gemini/OpenAI strict function calling)
rejeitam a resposta.
---
## 2. Produtos
**Controller REST:** `WC_REST_Products_Controller`
(`includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php`, `namespace =
'wc/v3'`, `rest_base = 'products'`), estende `WC_REST_Products_V2_Controller`. **Classe CRUD
nativa:** `WC_Product` (+ subclasses por tipo) e o data store
`WC_Product_Data_Store_CPT`
(`includes/data-stores/class-wc-product-data-store-cpt.php`). **Capability WordPress:**
`capability_type = 'product'`, `map_meta_cap = true` (`includes/class-wc-post-types.php:402`) —
gera `edit_product`/`edit_products`/`publish_products`/`read_private_products`/`delete_product`
etc.; atribuídas a `administrator` e `shop_manager` no install (`WC_Install::create_roles()`,
`includes/class-wc-install.php:2289-2340`).
### 2.1 Schema real (campos confirmados por leitura de `get_item_schema()`)
`type` (enum de `wc_get_product_types()`: `simple`, `grouped`, `external`, `variable`, mais os que
plugins registarem), `status` (`publish`/`draft`/`pending`/`private`/`future`/`auto-draft`/`trash`),
`featured`, `catalog_visibility` (`visible`/`catalog`/`search`/`hidden`), `sku`,
`global_unique_id` (GTIN/UPC/EAN/ISBN), `regular_price`/`sale_price`/`date_on_sale_from(_gmt)`/
`date_on_sale_to(_gmt)` (`price` e `on_sale` e `price_html` são readonly, calculados),
`virtual`/`downloadable` (+ `downloads[]`/`download_limit`/`download_expiry`),
`external_url`/`button_text` (só produtos `external`), `tax_status`/`tax_class`,
`manage_stock`/`stock_quantity`/`stock_status`/`backorders`/`low_stock_amount`/
`sold_individually`, `weight`/`dimensions{length,width,height}`, `shipping_class`,
`upsell_ids[]`/`cross_sell_ids[]`, `categories[]`/`tags[]` (id ou `{name}` para criar on-the-fly),
`images[]` (featured + galeria, aceita URL externo — faz sideload via `wc_rest_upload_image_from_url()`),
`attributes[]`/`default_attributes[]` (só produtos `variable`), `grouped_products[]` (só `grouped`),
`meta_data[]`. **Internal meta keys confirmados no data store** (não expostos individualmente, mas
confirma o que `manage_stock`/etc. persistem):
`_sku`, `_global_unique_id`, `_price`, `_regular_price`, `_sale_price`,
`_sale_price_dates_from(to)`, `total_sales`, `_tax_status`, `_tax_class`, `_manage_stock`,
`_stock`, `_stock_status`, `_backorders`, `_low_stock_amount`, `_sold_individually`, `_weight`,
`_length`/`_width`/`_height`, `_upsell_ids`, `_crosssell_ids`, `_purchase_note`,
`_default_attributes`, `_product_attributes`, `_virtual`, `_downloadable`
(`class-wc-product-data-store-cpt.php`, `$internal_meta_keys`).
### 2.2 Endpoints extra confirmados (além do CRUD standard)
- **`POST /products/(id)/duplicate`** — `duplicate_product()`
(`class-wc-rest-products-controller.php`, `register_routes()`): clona um produto via
`WC_Admin_Duplicate_Product::product_duplicate()`, status forçado a `draft`, nome com sufixo
`(copy)`.
- **`POST /products/batch`** — endpoint de lote (create/update/delete em array), definido na base
`WC_REST_Products_V2_Controller::register_routes()` (linhas ~216-227 do ficheiro V2); a v3
sobrepõe `batch_items()` só para diferir a contagem de termos (`wp_defer_term_counting()`) durante
o lote inteiro, por performance quando muitos produtos partilham categorias/tags.
- **`GET /products/suggested-products`** — sugestões cacheadas (`with_cache()` wrapper), fora do
âmbito de uma réplica inicial (é uma feature de UX do editor de produto, não de dados).
- **`POST /products/(id)/variations/generate`** — gera em massa todas as combinações de variações a
partir dos atributos do produto pai, com `default_values` opcional e `delete: bool` para apagar
variações órfãs (`class-wc-rest-product-variations-controller.php::register_routes()`).
### 2.3 Tabela de ability proposta — Produtos
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
| `woo-products-read` | Lista/procura produtos (nome, SKU parcial/exacto, `search_name_or_sku`), filtra por tipo/categoria/tag/classe-de-envio/atributo+termo/`stock_status`/`on_sale`/`featured`/gama de preço; devolve um produto/variação completo por ID | `WC_REST_Products_Controller::prepare_objects_query()`/`get_object()` (`wc/v3/products`); `wc_get_product()`/`wc_get_products()` como alternativa nativa | readonly, idempotente |
| `woo-products-write` | Cria/actualiza um produto: preço, stock (`manage_stock`/`stock_quantity`/`backorders`/`low_stock_amount`), estado, visibilidade, imagens (upload por URL), categorias/tags (cria on-the-fly), atributos + `default_attributes` (variáveis), upsell/cross-sell, downloads, campos COGS (Cost of Goods Sold, se activo) | `WC_REST_Products_Controller::prepare_object_for_database()`; `WC_Product::save()` | write, não-idempotente; nunca apaga dados existentes por omissão (merge de arrays como `meta_data` via `MetaDataUtil::update()`) |
| `woo-product-duplicate` | Clona um produto completo para rascunho (`(copy)`) | `duplicate_product()` → `WC_Admin_Duplicate_Product::product_duplicate()` | write, não-destrutivo (cria novo, não altera o original) |
| `woo-products-delete` | Envia para o lixo ou apaga definitivamente um produto (e cascata para as suas variações) | `WC_REST_CRUD_Controller::delete_item()` — trash se `EMPTY_TRASH_DAYS > 0` e não `force`, senão apagar definitivo | **destructive** quando `force=true` (irreversível); trash é recuperável |
| `woo-product-variations` | CRUD de uma variação individual + geração em massa de variações a partir de combinações de atributos, com opção de apagar variações não usadas | `WC_REST_Product_Variations_Controller` (`wc/v3/products/<id>/variations[/generate]`) | write; `generate` com `delete:true` é destrutivo (apaga variações existentes não geradas) |
| `woo-product-taxonomy` | Lista/cria/edita/apaga categorias, tags, atributos globais, termos de atributo, e classes de envio | `WC_REST_Product_Categories_Controller`, `...Tags...`, `...Attributes...`, `...Attribute_Terms...`, `...Shipping_Classes_Controller` (todos `wc/v3`); capability `manage_product_terms`/`edit_product_terms`/`delete_product_terms` (`wc_rest_check_manager_permissions('attributes',...)` + `wc_rest_check_product_term_permissions()`, `includes/wc-rest-functions.php:341` e `:298`) | write nos termos; delete de um termo usado por produtos não apaga os produtos, só a associação |
| `woo-products-batch` | Cria/actualiza/apaga vários produtos numa só chamada (limite prático imposto pelo consumidor, WooCommerce não limita explicitamente) | Rota `/products/batch`, `batch_items()` (`class-wc-rest-products-v2-controller.php`) | write multi-objecto; falha parcial por item — cada linha do resultado precisa de ser inspeccionada individualmente |
---
## 3. Encomendas
**Controller REST:** `WC_REST_Orders_Controller`
(`includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller.php`, `rest_base =
'orders'`), estende `WC_REST_Orders_V2_Controller`. **Classe CRUD nativa:** `WC_Order` +
`WC_Order_Item_Product`/`_Fee`/`_Shipping`/`_Coupon`. **Capability:** `capability_type =
'shop_order'`, `map_meta_cap = true` (`includes/class-wc-post-types.php:462`).
### 3.1 Estados reais (não inventados — via `wc_get_order_statuses()`)
Confirmado em `includes/wc-order-functions.php:104-118`, mapa `OrderInternalStatus::*` filtrável
por `wc_order_statuses`:
| Status (sem prefixo `wc-`) | Label | Nota |
|---|---|---|
| `pending` | Pending payment | estado inicial antes de pagamento confirmado |
| `processing` | Processing | pagamento recebido, aguarda fulfilment — conta como "pago" (`wc_get_is_paid_statuses()`) |
| `on-hold` | On hold | pagamento offline pendente de confirmação manual |
| `completed` | Completed | fulfilment concluído — conta como "pago" |
| `cancelled` | Cancelled | |
| `refunded` | Refunded | |
| `failed` | Failed | |
Além destes 7, `WC_REST_Orders_Controller::get_collection_params()` aceita também `any` e o
interno `trash` (`Automattic\WooCommerce\Enums\OrderStatus::TRASH`) para filtragem de listagem;
e existe ainda um estado interno `checkout-draft` (usado por `save_object()` como fallback quando
um `WC_Data_Exception` ocorre a meio da criação — não é um estado que se defina intencionalmente
via API, é um mecanismo de recuperação de falha de checkout).
### 3.2 Itens da encomenda e ciclo de escrita
A prop `line_items`/`shipping_lines`/`fee_lines`/`coupon_lines` cada uma aceita um array de linhas;
`prepare_object_for_database()` (`class-wc-rest-orders-controller.php`) itera cada item: se
`item_is_null()` ou `quantity===0`, remove-o (`remove_item()`, que valida que o `id` pertence
realmente à encomenda antes de o deixar remover — `woocommerce_rest_invalid_item_id` senão); caso
contrário `set_item()` (cria ou actualiza). Cupões são tratados à parte por
`calculate_coupons()` — valida cada código via `WC_Discounts::is_coupon_valid()`, remove **todos**
os cupões actuais e reaplica a lista completa recebida (não é um merge incremental — enviar
`coupon_lines` sem um código existente **remove-o** da encomenda). `set_paid: true` no request
chama `payment_complete()` se a encomenda ainda precisar de pagamento. `manual_update: true` marca
a nota de mudança de estado como "added by user" em vez de automática.
### 3.3 Devoluções (refunds)
**Controller:** `WC_REST_Order_Refunds_Controller`
(`includes/rest-api/Controllers/Version3/class-wc-rest-order-refunds-controller.php`,
`wc/v3/orders/<order_id>/refunds`). Criar um refund chama a função nativa `wc_create_refund()`
(`includes/wc-order-functions.php:559`) com `order_id`, `amount`, `reason`, `line_items`
(devolução parcial por item), `refund_payment` (tenta reembolso automático via gateway, se
suportado) e `restock_items` (repõe stock dos itens devolvidos). `amount < 0` é rejeitado
explicitamente (`woocommerce_rest_invalid_order_refund`).
### 3.4 Notas da encomenda
Confirmado apenas por registo no router (`Server.php`, `'order-notes' => 'WC_REST_Order_Notes_Controller'`
em `wc/v1`/`wc/v2`/`wc/v3`) — ficheiro não lido linha a linha nesta tarefa; é o mecanismo padrão
de comentários internos/ao cliente numa encomenda (`WC_Order::add_order_note()`).
### 3.5 Tabela de ability proposta — Encomendas
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
| `woo-orders-read` | Lista/procura encomendas por estado (um ou vários, incl. `any`), por `created_via`, por número parcial (via `wc-analytics/orders`, ver §6), devolve uma encomenda completa (billing/shipping, itens, totais, meta) | `WC_REST_Orders_Controller::prepare_objects_query()`/`get_object()` (`wc/v3/orders`); `wc_get_orders()` nativo | readonly |
| `woo-orders-write` | Cria/edita encomenda: billing/shipping, `line_items`/`shipping_lines`/`fee_lines` (add/update/remove por linha), `customer_id`, `set_paid`, meta, dados COGS (se activo) | `prepare_object_for_database()`/`save_object()` (`WC_Order::save()`, `calculate_totals()`) | write, não-idempotente; remoção de linha por `quantity:0` é irreversível sem novo pedido |
| `woo-order-status` | Muda o estado de uma encomenda (com opção `manual_update` para a nota registar como acção humana) | `WC_Order::set_status()`, disparado de `save_object()` quando `request['status']` presente | write; transições como `cancelled`/`refunded` podem disparar hooks de terceiros (emails, restock) fora do controlo directo da ability |
| `woo-order-coupons` | Aplica/substitui a lista completa de cupões de uma encomenda (valida cada código antes de aplicar) | `calculate_coupons()`; `WC_Discounts::is_coupon_valid()`, `WC_Order::apply_coupon()`/`remove_coupon()` | write; **enviar sem um cupão existente remove-o** — comportamento não-óbvio a documentar explicitamente no schema da ability |
| `woo-order-refunds` | Cria uma devolução total/parcial (por linha), com reembolso automático via gateway e reposição de stock opcionais | `WC_REST_Order_Refunds_Controller::prepare_object_for_database()` → `wc_create_refund()` (`wc-order-functions.php:559`) | **destructive**-adjacente: pode mover dinheiro real através do gateway de pagamento se `refund_payment:true` |
| `woo-orders-delete` | Envia para o lixo ou apaga definitivamente uma encomenda | `WC_REST_CRUD_Controller::delete_item()` (mesma lógica trash/force que produtos) | **destructive** quando `force=true` |
| `woo-order-notes` | Lê/adiciona notas internas ou ao cliente numa encomenda | `WC_REST_Order_Notes_Controller` (`wc/v3/orders/<id>/notes`) — registo confirmado em `Server.php`, corpo não lido | write (notas ao cliente disparam email) |
| `woo-orders-batch` | Cria/actualiza/apaga várias encomendas numa só chamada | Herdado de `WC_REST_CRUD_Controller`/`WC_REST_Posts_Controller` (mesmo padrão `/batch` dos produtos) | write multi-objecto |
---
## 4. Clientes
**Controller REST:** `WC_REST_Customers_Controller`
(`includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller.php`), estende
`WC_REST_Customers_V2_Controller`. **Diferença estrutural face a Produtos/Encomendas: um
"cliente" WooCommerce NÃO é um post type — é um utilizador WordPress com role `customer`.**
Confirmado: a gate de permissões usa `wc_rest_check_user_permissions()`
(`includes/wc-rest-functions.php:262`), não `wc_rest_check_post_permissions()`. **Classe CRUD
nativa:** `WC_Customer` (`includes/class-wc-customer.php`), também `WC_Data`-based, mas persiste
em `wp_users`/`wp_usermeta`, não `wp_posts`.
### 4.1 Capabilities reais (não `manage_woocommerce` genérico)
`wc_rest_check_user_permissions()` mapeia: `read`→`list_users`, `create`→`create_customers`,
`edit`→`edit_users`, `delete`→`delete_users`. **Nuance de segurança confirmada no código**
(`includes/wc-rest-functions.php:277-291`): quando o utilizador actual tem a role `shop_manager` e
a acção é `edit`/`delete`, a gate é **mais estrita** que a capability WordPress crua — só é
permitida se o alvo tiver a role `customer` (filtrável via `woocommerce_shop_manager_editable_roles`)
ou for o próprio utilizador. Ou seja: um `shop_manager` não pode, por esta via, editar/apagar outro
`administrator` ou `shop_manager`, mesmo tendo `edit_users`/`delete_users` a nível WordPress. É
também **multisite-aware** desde a 9.4.0: `Users::get_user_in_current_site()` revoga a permissão se
o alvo não pertencer ao site actual.
### 4.2 Schema real (confirmado por `get_item_schema()`)
`email`, `first_name`/`last_name`, `role` (readonly), `username`, `password` (write-only, contexto
`edit`), `billing{first_name,last_name,company,address_1,address_2,city,state,postcode,country,
email,phone}`, `shipping{...mesmos campos, sem email}`, `is_paying_customer` (readonly),
`avatar_url` (readonly), `meta_data[]`. **Sem histórico de encomendas embutido no schema base** —
isso só existe agregado na variante Analytics (§6.4).
### 4.3 Tabela de ability proposta — Clientes
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
| `woo-customers-read` | Lista/procura clientes (utilizadores role `customer`), devolve perfil completo (contactos, billing/shipping, `is_paying_customer`, meta) | `WC_REST_Customers_Controller` (`wc/v3/customers`); `new WC_Customer($user_id)` nativo | readonly |
| `woo-customers-write` | Cria/actualiza cliente: nome, email, username, password, endereços billing/shipping, meta | `WC_REST_Customers_V2_Controller::prepare_object_for_database()`/`save_object()`; `WC_Customer::save()` | write; criação de utilizador WordPress real (não só dados WooCommerce) |
| `woo-customer-addresses` | Actualiza só billing ou só shipping de um cliente, sem tocar no resto do perfil | Subconjunto do schema `billing`/`shipping` acima — proposta de ability mais granular para evitar sobrescrever password/username sem intenção | write |
| `woo-customers-delete` | Apaga um utilizador cliente | `WC_REST_CRUD_Controller`-equivalente para users (herdado de `WP_REST_Users_Controller` na prática do WordPress core, não uma classe própria WooCommerce) | **destructive**, irreversível — apagar um utilizador WordPress apaga a conta de login, não só o "perfil de cliente" |
| `woo-customer-history` | Agrega `orders_count`, `total_spend`, `avg_order_value`, `date_registered`, `date_last_active`, `date_last_order` para um cliente | **Não é dado pela `WC_Customer` base** — vem da tabela de analytics `wp_wc_customer_lookup`, populada por `CustomersDataStore::update_registered_customer()` (`src/Admin/API/Reports/Customers/DataStore.php`, colunas confirmadas linhas 50-93); requer feature `analytics` activa (§6) | readonly; **depende de `wc-analytics` estar carregado** — não confundir com o schema base do cliente |
---
## 5. Cupões / Descontos
**Controller REST:** `WC_REST_Coupons_Controller`
(`includes/rest-api/Controllers/Version3/class-wc-rest-coupons-controller.php`), estende
`WC_REST_Coupons_V2_Controller`. **Classe CRUD nativa:** `WC_Coupon`
(`includes/class-wc-coupon.php`). **Capability:** `capability_type = 'shop_coupon'`
(`includes/class-wc-post-types.php:525`).
### 5.1 Tipos de desconto reais (via `wc_get_coupon_types()`)
Confirmado em `includes/wc-coupon-functions.php:21-31`, filtrável por
`woocommerce_coupon_discount_types`:
| `discount_type` | Label | Comportamento (`WC_Coupon::get_discount_amount()`) |
|---|---|---|
| `percent` | Percentage discount | percentagem do valor do item/carrinho |
| `fixed_cart` | Fixed cart discount | valor fixo dividido proporcionalmente entre linhas por peso de subtotal (com/sem imposto conforme `wc_prices_include_tax()`) — **o default** (`$this->data['discount_type'] = 'fixed_cart'`) |
| `fixed_product` | Fixed product discount | valor fixo por unidade de produto elegível, `min(amount, preço)` |
`percent_product` é aceite por retrocompatibilidade e silenciosamente convertido para `percent`
(`set_discount_type_core()`, `includes/class-wc-coupon.php:600-608`). Se `discount_type==='percent'`
e `amount > 100`, o `WC_Coupon` recusa a gravação (`coupon_invalid_amount`).
### 5.2 Campos de restrição confirmados (schema/data props do coupon)
Além de `code`/`amount`/`discount_type`/`description`, o `WC_Coupon` (visto nos props por omissão
e no método `is_coupon_valid()` referenciado por `WC_REST_Orders_Controller::calculate_coupons()`)
gere: `date_expires`, `usage_count`/`usage_limit`/`usage_limit_per_user`/`limit_usage_to_x_items`,
`individual_use`, `free_shipping`, `exclude_sale_items`, restrições por produto/categoria
(include/exclude), por valor mínimo/máximo de carrinho, e por email de cliente
(`customer_email`/`email_restrictions`).
### 5.3 Tabela de ability proposta — Cupões
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
| `woo-coupons-read` | Lista/procura cupões por código exacto (`wc_get_coupon_id_by_code()`, usado em `prepare_objects_query()`) ou estado; devolve um cupão completo (tipo de desconto, valor, restrições, uso) | `WC_REST_Coupons_Controller::prepare_objects_query()` (`wc/v3/coupons`); `new WC_Coupon($code)` nativo | readonly |
| `woo-coupons-write` | Cria/actualiza cupão: código, `discount_type` (3 valores válidos), `amount`, datas de expiração, `free_shipping`, `individual_use`, limites de uso, restrições produto/categoria/valor-mínimo/email | `WC_REST_Coupons_V2_Controller::prepare_object_for_database()`; `WC_Coupon::save()` | write; `amount>100` com `discount_type:percent` é recusado pelo próprio `WC_Coupon` (validação de domínio, não HTTP) |
| `woo-coupons-delete` | Envia para o lixo ou apaga definitivamente um cupão | `WC_REST_CRUD_Controller::delete_item()` | **destructive** quando `force=true`; cupão apagado deixa de validar em encomendas futuras mas não afecta descontos já aplicados a encomendas passadas |
| `woo-coupon-validate` | Valida um código de cupão contra uma encomenda/carrinho concreto (elegibilidade, expirado, limite atingido) sem o aplicar | `WC_Discounts::is_coupon_valid()`, usado internamente por `calculate_coupons()` — proposta de ability read-only exposta directamente, útil para um agente confirmar "este cupão ainda serve?" antes de o aplicar numa encomenda | readonly |
---
## 6. Relatórios / Analytics (`wc-analytics/*`)
O namespace **existe e é substancial** — confirmado por leitura de
`src/Admin/API/Init.php::rest_api_init_wc_analytics()`. Carregado por
`RestApiUtil::lazy_load_namespace('wc-analytics', ...)` (só quando um pedido bate nesse namespace,
feature de performance — `includes/wc-rest-functions.php::wc_rest_should_load_namespace()`
confirma `wc-analytics` na lista de namespaces conhecidos que podem ficar por carregar).
### 6.1 Dois grupos de controllers
1. **Sempre registados** (não dependem da feature `analytics`): `Notes`, `NoteActions`, `Coupons`,
`Data`/`DataCountries`/`DataDownloadIPs`, `Orders`, `Products`, `ProductAttributes`,
`ProductAttributeTerms`, `ProductCategories`, `ProductVariations`, `ProductReviews`,
`ProductsLowInStock`, `SettingOptions`, `Taxes`. Vários destes **estendem directamente os
controllers `wc/v3`** — confirmado por leitura completa de `src/Admin/API/Orders.php`:
`class Orders extends \WC_REST_Orders_Controller` (namespace reescrito para `wc-analytics`),
adicionando só um parâmetro `number` (procura por número de encomenda parcial, via SQL directo
à tabela HPOS ou meta legado) — herda a **mesma** gate de capability `shop_order` do §3, não
uma nova.
2. **Só quando `Features::is_enabled('analytics')`**: `Customers`, `Leaderboards`,
`Reports\Controller` (catálogo de descoberta dos sub-relatórios) e um `Reports\*\Controller`
por domínio: **Products, Variations, Revenue (`/stats`), Orders (`/stats`), Categories, Taxes,
Coupons, Stock, Downloads, Customers**, mais `Import`/`Export`, `AnalyticsImports` (estado de
importações falhadas), e `PerformanceIndicators` (registado por último, agrega indicadores de
todos os `/stats` já registados).
### 6.2 Capability real para relatórios (não `manage_woocommerce`)
Confirmado em `includes/rest-api/Controllers/Version1/class-wc-rest-reports-v1-controller.php:60-63`
(base herdada por toda a família de reports, incl. o `GenericController` de que
`Reports\Controller` deriva): `get_items_permissions_check()` chama
`wc_rest_check_manager_permissions('reports', 'read')` → capability
**`view_woocommerce_reports`** (`includes/wc-rest-functions.php:343`), **distinta** de
`manage_woocommerce`. Um utilizador pode ter acesso de leitura a relatórios sem ter acesso de
gestão às definições da loja — desenho de menor privilégio já presente no core.
### 6.3 Data stores de agregação confirmados
`src/Admin/API/Init.php::add_data_stores()` regista, via o filtro `woocommerce_data_stores`:
`report-revenue-stats`, `report-orders`, `report-orders-stats`, `report-products`,
`report-variations`, `report-products-stats`, `report-variations-stats`, `report-categories`,
`report-taxes`, `report-taxes-stats`, `report-coupons`, `report-coupons-stats`,
`report-downloads`, `report-downloads-stats`, `report-customers`, `report-customers-stats`,
`report-stock-stats`. Cada um persiste em tabelas próprias sob `wp_wc_order_stats`,
`wp_wc_customer_lookup`, etc. (não `wp_posts`/`wp_postmeta`) — populadas por processamento
assíncrono (`ReportsSync.php`, `src/Internal/Admin/`), não em tempo real na escrita.
### 6.4 Tabela de ability proposta — Analytics
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
| `woo-analytics-orders` | Lista encomendas com filtro por número parcial, além dos filtros standard de `wc/v3/orders` | `Automattic\WooCommerce\Admin\API\Orders extends \WC_REST_Orders_Controller` | readonly; mesma capability `shop_order` do §3, não `view_woocommerce_reports` |
| `woo-analytics-revenue-stats` | Séries temporais de receita, agregadas por intervalo (dia/semana/mês) | `Automattic\WooCommerce\Admin\API\Reports\Revenue\Stats\Controller` (`wc-analytics/reports/revenue/stats`); requer feature `analytics` | readonly, `view_woocommerce_reports` |
| `woo-analytics-products-stats` \| `-variations-stats` \| `-categories-stats` \| `-taxes-stats` \| `-coupons-stats` \| `-downloads-stats` \| `-customers-stats` \| `-stock-stats` | Relatórios detalhados + estatísticas agregadas por domínio (um par `<domínio>` + `<domínio>/stats` cada) | `Reports\{Products,Variations,Categories,Taxes,Coupons,Downloads,Customers,Stock}\Controller` (+ `\Stats\Controller` respectivo) — 8 pares confirmados em `Init.php::rest_api_init_wc_analytics()` | readonly, `view_woocommerce_reports` |
| `woo-analytics-customer-lookup` | Perfil de cliente enriquecido com `orders_count`/`total_spend`/`avg_order_value`/`date_registered`/`date_last_active`/`date_last_order` | `Reports\Customers\Controller` + `DataStore.php` (colunas confirmadas §4.3) | readonly |
| `woo-analytics-performance-indicators` | Endpoint agregador "dashboard" — pede vários indicadores de vários `/stats` numa só chamada em lote | `Reports\PerformanceIndicators\Controller` (registado por último, depois de todos os `/stats`) | readonly |
| `woo-analytics-low-stock` | Lista produtos abaixo do limiar de stock baixo (`low_stock_amount`/global), útil para alertas | `Automattic\WooCommerce\Admin\API\ProductsLowInStock` — registado sempre-on (não depende da feature `analytics`) | readonly |
**Nota de prioridade para a réplica:** dado que toda a família `Reports\*` depende da feature
`analytics` **e** de tabelas de agregação populadas por um processamento assíncrono próprio
(`ReportsSync`, não lido em detalhe nesta tarefa), replicar isto do zero (sem depender do
WooCommerce já as ter populado) seria um subsistema por si só — para uma réplica que corre **sobre
um WooCommerce já instalado e com Analytics activo**, a decisão certa é **ler as tabelas/API já
populadas pelo WooCommerce**, não reimplementar o pipeline de sync.
---
## 7. Webhooks
**Classe CRUD nativa:** `WC_Webhook` (`includes/class-wc-webhook.php`, estende
`WC_Legacy_Webhook`). **Controller REST:** `WC_REST_Webhooks_Controller`
(`includes/rest-api/Controllers/Version3/class-wc-rest-webhooks-controller.php`, muito fino — só
sobrepõe `get_default_api_version()` para `wp_api_v3`, herda todo o resto de
`WC_REST_Webhooks_V2_Controller`). **Capability:** `wc_rest_check_manager_permissions('webhooks',...)`
→ `manage_woocommerce` (`includes/wc-rest-functions.php:349`).
### 7.1 Modelo de dados real (confirmado por `$data` default em `WC_Webhook`)
`status` (`active`/`paused`/`disabled` — `wc_get_webhook_statuses()`,
`includes/wc-webhook-functions.php`), `delivery_url`, `secret` (para assinatura HMAC do payload),
`name`, `topic` (formato `<resource>.<event>`), `hooks` (hooks WordPress internos ligados a este
topic), `resource`/`event` (derivados do topic), `failure_count`, `user_id` (autor), `api_version`
(default `3`), `pending_delivery`.
### 7.2 Validação de topic — não é livre texto
Confirmado em `wc_is_webhook_valid_topic()` (`includes/wc-webhook-functions.php`, linhas ~90-160):
recursos por omissão são **`coupon`, `customer`, `order`, `product`** (filtráveis via
`woocommerce_valid_webhook_resources`); eventos por omissão são **`created`, `updated`, `deleted`,
`restored`, `published`** (filtráveis via `woocommerce_valid_webhook_events`). Um par
recurso+evento **ambos por omissão** só é válido se tiver um hook WordPress realmente registado —
combinações "fantasma" como `order.published` ou `customer.restored` são **explicitamente
rejeitadas** mesmo sendo sintacticamente válidas, para não deixar o utilizador criar um webhook
"Active" que nunca dispara. Tópicos de acção livre (`action.woocommerce_*`/`action.wc_*`) são
sempre aceites, excepto uma pequena blocklist (`action.woocommerce_login_credentials`, etc.).
### 7.3 Entrega assíncrona por omissão
`wc_webhook_execute_queue()` (hook `shutdown`) enfileira entregas via `WC()->queue()`
(`Action Scheduler`), com deduplicação de 10 minutos por webhook — evita reentregas em rajada se
várias mutações do mesmo objecto ocorrerem no mesmo request. Entrega síncrona só se o filtro
`woocommerce_webhook_deliver_async` for forçado a `false`.
### 7.4 Tabela de ability proposta — Webhooks
| Área | O que cobre | Endpoints/classes reais | Nível de risco/destructive |
|---|---|---|---|
| `woo-webhooks-read` | Lista/lê webhooks configurados (URL, topic, estado, contagem de falhas) — **nunca devolve o `secret`** em texto claro (mascarar, seguindo o padrão já usado nesta base de código para `access_token`/`credential` noutras integrações) | `WC_REST_Webhooks_Controller` (`wc/v3/webhooks`); `wc_get_webhook()` nativo | readonly |
| `woo-webhooks-write` | Cria/actualiza um webhook: `topic` (validado contra `wc_is_webhook_valid_topic()`), `delivery_url`, `status`, `secret` (gerado automaticamente se omitido) | `WC_Webhook::save()`; validação de topic em `wc-webhook-functions.php` | write; um `delivery_url` malicioso pode ser usado para SSRF/exfiltração — **reutilizar `EMCP_Tools_Url_Guard`** já documentado em `docs/05-REDIRECTS-SEARCH-LEDGER.md` §6 para validar o destino antes de gravar |
| `woo-webhooks-delete` | Apaga um webhook | Herdado de `WC_REST_CRUD_Controller` | **destructive**, irreversível (histórico de entregas fica órfão) |
| `woo-webhook-deliveries` | Lista o histórico de entregas de um webhook (tentativas, resposta HTTP, duração) — útil para diagnóstico "porque é que este webhook não chegou ao destino" | `WC_REST_Webhook_Deliveries_V1_Controller`/`_V2_Controller` (`wc/v1`/`wc/v2` apenas — **não existe em `wc/v3`**, confirmado por ausência no mapa `get_v3_controllers()` de `Server.php`; uma réplica teria de ler directamente a tabela de log de entregas ou usar `wc/v2`) | readonly |
---
## 8. Segurança / gating recomendado
Resumo das capabilities WordPress **reais** confirmadas nesta tarefa (não inventadas), por domínio:
| Domínio | `capability_type` do post type / mecanismo | Capabilities geradas usadas pela REST API | Onde confirmado |
|---|---|---|---|
| Produtos | `product` (`map_meta_cap: true`) | `read_private_products`, `publish_products`, `edit_product`, `delete_product`, `edit_others_products` (batch) | `class-wc-post-types.php:402`; `wc_rest_check_post_permissions()` (`wc-rest-functions.php:229`) |
| Encomendas | `shop_order` (`map_meta_cap: true`) | `read_private_shop_orders`, `publish_shop_orders`, `edit_shop_order`, `delete_shop_order`, `edit_others_shop_orders` | `class-wc-post-types.php:462`; mesma função `wc_rest_check_post_permissions()` |
| Cupões | `shop_coupon` (`map_meta_cap: true`) | `read_private_shop_coupons`, `publish_shop_coupons`, `edit_shop_coupon`, `delete_shop_coupon` | `class-wc-post-types.php:525` |
| Clientes | **utilizador WordPress**, não post type | `list_users`, `create_customers`, `edit_users`, `delete_users` + gate extra `shop_manager`-só-edita-`customer` | `wc_rest_check_user_permissions()` (`wc-rest-functions.php:262-291`) |
| Categorias/Tags/Atributos de produto | taxonomia (`product_cat`/`product_tag`/atributos globais) | `manage_product_terms`, `edit_product_terms`, `delete_product_terms` | `class-wc-post-types.php:129-132` (e repetido nas outras 3 taxonomias) |
| Relatórios/Analytics (`reports`) | genérico | **`view_woocommerce_reports`** (distinto de `manage_woocommerce`) | `wc_rest_check_manager_permissions()` (`wc-rest-functions.php:341-349`); herdado por toda a família `Reports\*` via `class-wc-rest-reports-v1-controller.php:60-63` |
| Definições/System Status/Shipping/Payment Gateways/Webhooks | genérico | `manage_woocommerce` | mesma função, mesmo ficheiro |
**Recomendações de gating para a réplica** (aplicando o padrão já documentado em
`docs/08-INTEGRACOES-TERCEIROS.md` §0):
1. **Condição de registo do grupo inteiro**: `class_exists('WooCommerce') &&
function_exists('wc_get_product')` (ou equivalente) — WooCommerce activo, sem gate de licença
própria (é um plugin gratuito de terceiros, ao contrário de ACF Pro/Meta Box Pro).
2. **Gate coarse no MCP = a capability mais permissiva do domínio** (`edit_products` para
`woo-products-write`, `edit_users`+`create_customers` para clientes, `manage_woocommerce` para
webhooks) — a gate **fina** por operação deve reflectir exactamente as capabilities reais da
tabela acima, não um `manage_options` genérico que sobre-restringe (ex.: um utilizador com
`view_woocommerce_reports` mas sem `manage_woocommerce` deve conseguir chamar
`woo-analytics-*-read`, mas não `woo-webhooks-write`).
3. **Nunca reexpor o `secret` de um webhook em texto claro** — seguir o mesmo padrão já aplicado a
`access_token` de contas sociais e `credential` de custom apps documentado nas descrições das
ferramentas MCP existentes neste ecossistema.
4. **`woo-order-coupons` e `woo-orders-write` precisam de aviso explícito no schema** sobre o
comportamento "substituição total, não merge" de `coupon_lines`/`line_items` — é o tipo de
comportamento não-óbvio que já causou confusão documentada noutras integrações desta série
(ver ACF `update-field-group`, doc08 §1.3, para o precedente de "campos imutáveis" bem
documentados no schema para evitar o mesmo erro).
5. **Change ledger**: todas as escritas write/delete desta secção devem passar pelo mesmo
`Change_Log`/`Change_Recorder` já documentado em `docs/05-REDIRECTS-SEARCH-LEDGER.md` §4 —
`WC_Product`/`WC_Order`/`WC_Coupon` expõem `get_data()` (snapshot completo do objecto) **antes**
de qualquer `save()`, o que torna trivial um novo tipo de rollback `woo-product-before-image`/
`woo-order-before-image`/`woo-coupon-before-image` no mesmo padrão dos 11 tipos já existentes.
Clientes (utilizadores WordPress) já têm cobertura possível via o tipo `user-create`/`post-fields`
equivalente, se a réplica tratar edição de perfil de cliente como uma variante de edição de
utilizador.
---
## 9. Blueprint para réplica
### 9.1 Prioridade de construção (por valor/esforço, assumindo Fase 0 do doc00 já feita)
1. **`woo-products-read` + `woo-orders-read` + `woo-customers-read`** — três abilities read-only,
zero risco, cobrem o caso de uso mais comum ("o agente precisa de ver o estado da loja"). Sem
dependência de change ledger. Construir primeiro.
2. **`woo-products-write` / `woo-orders-write` / `woo-coupons-write`** — o trio de escrita
CRUD standard. Reaproveitar 1:1 o padrão `prepare_object_for_database()` → `->save()` já visto
nos 3 controllers reais (a lógica de mapear `request[key] → $object->set_{key}()` é
directamente portável). Requer change ledger (before-image) já pronto.
3. **`woo-order-status` + `woo-order-refunds`** — separados de `woo-orders-write` de propósito:
mudar estado e criar devolução são as duas acções de maior impacto de negócio numa encomenda
(podem mover dinheiro real via gateway), e merecem `permission_callback`/`confirm:true` mais
estritos do que uma edição de morada de entrega.
4. **`woo-coupons-read`/`woo-coupon-validate`** — baixo risco, alto valor para agentes de
apoio-ao-cliente ("este código ainda é válido?").
5. **`woo-webhooks-read`/`woo-webhooks-write`** — depois de ter o `EMCP_Tools_Url_Guard`
equivalente pronto (§8 nota 3), dado o vector SSRF real do campo `delivery_url`.
6. **`woo-analytics-*`** — só depois de confirmar que a instalação alvo tem a feature `analytics`
activa e as tabelas de agregação já populadas; caso contrário, cair para computar as mesmas
métricas por leitura directa de encomendas via `wc_get_orders()` com `date_query` (mais lento,
mas sem dependência de infra-estrutura extra).
7. **`woo-product-taxonomy`/`woo-product-variations`/`woo-orders-batch`/`woo-products-batch`** —
utilitários de produtividade, construir por último; nenhum introduz capability nova face às
anteriores.
### 9.2 Reaproveitamento explícito dos padrões já documentados
- **`normalize_result()`** (doc00 §5) é **obrigatório** aqui, mais do que em qualquer outro
domínio desta série — confirmado nesta tarefa que `WC_REST_CRUD_Controller::get_items()` devolve
sempre um array indexado (nunca um mapa), e vários controllers de relatório devolvem array de
topo. Sem o wrapper, todas as abilities `*-read` desta secção partem em clientes MCP estritos.
- **Dispatcher de dois braços `<domínio>-read`/`<domínio>-write`** (doc08 §0) é a forma certa de
empacotar isto — WooCommerce tem demasiadas operações por domínio (produtos sozinho tem
~10 operações reais: list/get/create/update/delete/duplicate/batch/variations-CRUD/generate) para
justificar uma ability por operação; seguir exactamente o mesmo catálogo de descoberta
`{operation?, arguments?}` já usado por ACF/Meta Box/Forms/SEO.
- **Change ledger unificado** (doc05 §4) — `WC_Product`/`WC_Order`/`WC_Coupon` são todos `WC_Data`
com `get_data()` nativo, o que torna a captura de before-image mais simples aqui do que em
qualquer outra integração de terceiros desta série (ACF precisa de reconstruir o estado a partir
de field groups dispersos; aqui é um único array).
- **`EMCP_Tools_Url_Guard`** (doc05 §6) — reutilizar directamente para validar `delivery_url` de
webhooks antes de gravar; é exactamente o caso de uso para o qual a Camada 2 (`validate()`) foi
desenhada (SSRF via campo controlado por utilizador que o próprio servidor depois contacta).
### 9.3 O que NÃO replicar sem decisão explícita
- **`wc/v4`** — instável, parcial (12 de ~50 recursos), atrás de uma feature flag ainda em
desenvolvimento pelo próprio WooCommerce. Uma réplica que se acople a `wc/v4` hoje arrisca
quebrar em cada minor release enquanto o WooCommerce a estabiliza. Usar `wc/v3` ou as classes
CRUD nativas.
- **Reimplementar o pipeline de sync de Analytics** — as tabelas `wp_wc_order_stats`/
`wp_wc_customer_lookup`/etc. já existem e são mantidas pelo próprio WooCommerce quando a feature
`analytics` está activa; uma réplica deve **ler**, nunca recalcular estas agregações do zero.
- **`woo-webhook-deliveries`** tal como existe em `wc/v1`/`wc/v2` — a WooCommerce já não o expõe em
`wc/v3`; construir uma ability nova para isto implica decidir se se lê a tabela de log
directamente via SQL (mais frágil a mudanças de schema interno) ou se se aceita a lacuna e se
documenta como "não disponível na API estável".
- **`suggested-products`** — é uma feature de UX do editor de produto do wp-admin (cache de
sugestões calculadas), não dados de domínio; sem valor claro para um agente MCP.
---
## 10. Fonte
Leitura directa (19-08-2026) do WooCommerce 11.0.1 instalado em `ecommerce.descomplicar.pt`
(`/home/ealmeida/ecommerce.descomplicar.pt/wp-content/plugins/woocommerce/`):
- `includes/rest-api/Server.php` (integral — mapa dos 4+1 namespaces e todos os controllers registados)
- `includes/rest-api/Controllers/Version3/class-wc-rest-crud-controller.php` (integral)
- `includes/rest-api/Controllers/Version3/class-wc-rest-products-controller.php` (integral, incl. schema completo)
- `includes/rest-api/Controllers/Version3/class-wc-rest-orders-controller.php` (integral)
- `includes/rest-api/Controllers/Version3/class-wc-rest-customers-controller.php` (integral, incl. schema completo)
- `includes/rest-api/Controllers/Version3/class-wc-rest-coupons-controller.php` (integral)
- `includes/rest-api/Controllers/Version3/class-wc-rest-product-variations-controller.php` (cabeçalho + `register_routes()`)
- `includes/rest-api/Controllers/Version3/class-wc-rest-order-refunds-controller.php` (cabeçalho + `prepare_object_for_database()`)
- `includes/rest-api/Controllers/Version3/class-wc-rest-webhooks-controller.php` (integral)
- `includes/rest-api/Controllers/Version2/class-wc-rest-products-v2-controller.php` (excerto: rota `/batch` + `batch_items()`)
- `includes/wc-rest-functions.php` (integral — todas as funções `wc_rest_check_*_permissions`)
- `includes/class-wc-post-types.php` (excertos: `capability_type` de `product`/`shop_order`/`shop_coupon`, `capabilities` das 4 taxonomias de produto)
- `includes/class-wc-install.php` (excertos: `create_roles()`, atribuição de capabilities a `shop_manager`/`administrator`)
- `includes/wc-order-functions.php` (excertos: `wc_get_order_statuses()`, `wc_get_is_paid_statuses()`, assinatura de `wc_get_orders()`/`wc_create_refund()`)
- `includes/wc-coupon-functions.php` (excerto: `wc_get_coupon_types()`)
- `includes/class-wc-coupon.php` (excertos: defaults, `is_type()`, `get_discount_amount()`, `set_discount_type_core()`, `get_short_info()`)
- `includes/class-wc-webhook.php` (cabeçalho + `$data` default)
- `includes/wc-webhook-functions.php` (integral)
- `includes/data-stores/class-wc-product-data-store-cpt.php` (cabeçalho + `$internal_meta_keys`)
- `includes/abstracts/abstract-wc-product.php` (excertos: `get_stock_status()`, `set_manage_stock()`, `set_stock_quantity()`, `backorders_allowed()`)
- `includes/class-wc-customer.php` (cabeçalho + `$data` default)
- `includes/wc-product-functions.php` (excerto: assinaturas `wc_get_products()`/`wc_get_product()`)
- `src/Admin/API/Init.php` (integral — bootstrap `wc-admin`/`wc-analytics`, listas de controllers, `add_data_stores()`)
- `src/Admin/API/Orders.php` (excerto: `class Orders extends \WC_REST_Orders_Controller`)
- `src/Admin/API/Reports/Controller.php` (excerto: catálogo `get_items()`)
- `src/Admin/API/Reports/GenericController.php` (integral)
- `src/Admin/API/Reports/Customers/DataStore.php` (excertos: colunas `orders_count`/`total_spend`/`avg_order_value`/`date_registered`/`date_last_active`)
- `includes/rest-api/Controllers/Version1/class-wc-rest-reports-v1-controller.php` (excerto: `register_routes()`+`get_items_permissions_check()`)
- `src/Internal/Admin/Loader.php` (excerto: import de `Reports\Orders\DataStore`, confirma existência do namespace `Automattic\WooCommerce\Admin\API\Reports`)
Listagens de directório (sem leitura de conteúdo, só confirmação de existência/inventário):
`includes/rest-api/Controllers/` (Version1-4 + Telemetry), `includes/rest-api/Controllers/Version3/`
(51 controllers), `includes/rest-api/Controllers/Version4/` (2 ficheiros — wrapper fino),
`src/Admin/API/` (44 ficheiros + 5 subdirectórios), `src/Admin/API/Reports/` (13 subdirectórios de
domínio + 13 ficheiros base), `src/Internal/Admin/` (confirma que `Analytics.php`/`Coupons.php` aí
não são routers REST, o router real é `src/Admin/API/Init.php`).
Cruzado com `docs/00-ARQUITECTURA.md` (padrão de registo de ability, `normalize_result`, nota
explícita sobre `WP_REST_Response::get_data()` de rotas WooCommerce), `docs/05-REDIRECTS-SEARCH-LEDGER.md`
§4 e §6 (change ledger, `EMCP_Tools_Url_Guard`), `docs/08-INTEGRACOES-TERCEIROS.md` §0 (padrão de
dispatcher de dois braços), e `docs/10-MODULOS-E-INVENTARIO-PRO.md` linha 470 (confirmação do gate
`class_exists() && EMCP_Tools_Woo_Integration::woo_active()`, sem module toggle dedicado — única
referência real ao EMCP Pro Woo Integration disponível nesta série, dado o código-fonte Pro não
estar acessível).
+9 -7
View File
@@ -1,10 +1,11 @@
# EMCP Tools — Mapeamento completo e blueprint de réplica
Índice e síntese de 11 documentos (~6 070 linhas), produzidos 19-08-2026 por leitura directa do
Índice e síntese de 12 documentos (~6 940 linhas), produzidos 19-08-2026 por leitura directa do
código-fonte `emcp-tools` v3.12.1 (build Free, `msrbuilds/elementor-mcp`, GPL-2.0-or-later),
instalado em `emanuelalmeida.pt` (`/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/emcp-tools/`).
Cruzado com `skill://emcp-tools` (auditoria de postura de segurança viva nos 3 sites do
ecossistema: `descomplicar.pt`, `emanuelalmeida.pt`, `starter.descomplicar.pt`).
instalado em `emanuelalmeida.pt` (`/home/ealmeida/emanuelalmeida.pt/wp-content/plugins/emcp-tools/`),
mais um doc adicional (11) fundamentado no WooCommerce 11.0.1 real instalado em
`ecommerce.descomplicar.pt`. Cruzado com `skill://emcp-tools` (auditoria de postura de segurança
viva nos 3 sites do ecossistema: `descomplicar.pt`, `emanuelalmeida.pt`, `starter.descomplicar.pt`).
**Objectivo desta série:** especificação funcional completa, ao nível de "o que este código faz e
porquê", para servir de base a uma réplica própria — seja fork (GPL permite) seja reescrita limpa.
@@ -29,10 +30,11 @@ copiar-1:1 / simplificar / deixar de fora.
| [08-INTEGRACOES-TERCEIROS](08-INTEGRACOES-TERCEIROS.md) | ACF, Meta Box, formulários (CF7 free), SEO (SlimSEO free) | 457 | ~30 (free) | Plugin de terceiros instalado |
| [09-STOCK-IMAGES-CLOUD-OAUTH](09-STOCK-IMAGES-CLOUD-OAUTH.md) | Unsplash/Pexels/Pixabay, EMCP Cloud (ligação/sync), servidor OAuth para MCP remoto | 654 | ~17 | Nenhuma |
| [10-MODULOS-E-INVENTARIO-PRO](10-MODULOS-E-INVENTARIO-PRO.md) | Sistema de módulos (toggles), Image Optimization, SVG Support, **inventário definitivo das 30 classes Pro-only** | 661 | n/a + 30 ausentes | — (estrutural + auditoria) |
| [11-WOOCOMMERCE-BLUEPRINT](11-WOOCOMMERCE-BLUEPRINT.md) | **Não é auditoria EMCP** — blueprint de abilities WooCommerce (produtos/encomendas/clientes/cupões/analytics/webhooks) fundamentado no WooCommerce real, para preencher o gap `EMCP_Tools_Woo_Integration` (Pro, código ausente). Exclui SEO/Yoast por pedido. | 878 | ~35 propostas | WooCommerce activo |
Total: **~6 070 linhas de documentação** cobrindo **~165 abilities** (número confirmado ao vivo em
`skill://emcp-tools` §7) sobre **~128 465 linhas de PHP** do plugin (`~50k` das quais são o SDK
Freemius bundled, irrelevante para réplica).
Total: **~6 940 linhas de documentação**, ~165 abilities EMCP confirmadas + ~35 abilities WooCommerce
propostas (doc 11, não implementadas no EMCP — blueprint próprio) sobre **~128 465 linhas de PHP**
do plugin `emcp-tools` (`~50k` das quais são o SDK Freemius bundled, irrelevante para réplica).
---