Order status customizado no Magento 2: states, statuses e workflow
Lojistas pensam com as próprias palavras: "aguardando estoque", "em produção", "pronto para retirada". O Magento tem o seu próprio ciclo de vida fixo por baixo. Order statuses customizados são o que liga os dois, e funcionam bem enquanto você respeitar a diferença entre state e status. Quando não respeita, o Magento sobrescreve o seu status em silêncio no próximo save e ninguém entende por quê.
Este artigo explica states e statuses no Magento Open Source e no Adobe Commerce 2.4.8, como criar statuses em código para que todos os ambientes fiquem iguais, como alterá-los com segurança e como avisar os clientes.
States e statuses
Um state faz parte do código do Magento. Ele decide o que pode acontecer com o pedido: dá para faturar, enviar, reembolsar, cancelar. Os states são constantes em Magento\Sales\Model\Order:
| Constante | Valor |
|---|---|
| STATE_NEW | new |
| STATE_PENDING_PAYMENT | pending_payment |
| STATE_PROCESSING | processing |
| STATE_COMPLETE | complete |
| STATE_CLOSED | closed |
| STATE_CANCELED | canceled |
| STATE_HOLDED | holded |
| STATE_PAYMENT_REVIEW | payment_review |
Você não consegue adicionar um state sem reescrever grandes partes do módulo de sales, e não deveria tentar.
Um status é um rótulo guardado no banco de dados. Cada status é atribuído a um ou mais states em sales_order_status_state, e cada state tem um status padrão. A coluna status do pedido é o que admins e clientes veem. A coluna state é o que o código verifica.
Então "em produção" não é uma nova etapa no Magento. É um status sob o state processing. O pedido se comporta exatamente como qualquer pedido em processing, só mostra um rótulo diferente.
Desenhando o workflow
Comece mapeando cada etapa do negócio para um state, antes de mexer no código. Para cada etapa, pergunte: o pedido ainda pode ser cancelado? Está pago? Já foi enviado? As respostas apontam o state:
- "Aguardando transferência bancária" cabe em
pending_paymentounew, dependendo de como o método de pagamento funciona. - "Aguardando estoque" e "em produção" normalmente cabem em
processing, quando o pedido está pago mas não foi enviado. - "Pronto para retirada" é complicado. Se o shipment já foi criado no Magento, o pedido talvez já esteja
complete.
Mantenha a lista curta. Todo status precisa de alguém ou de algo que mova pedidos para dentro e para fora dele. Statuses que ninguém atualiza viram ruído no grid de pedidos.
Criando statuses com um data patch
Você pode criar statuses no admin em Stores, Settings, Order Status. Serve para algo pontual, mas aí staging, local e produção se afastam. Um data patch mantém tudo em código. Declarative schema e data patches cobre patches em geral.
<?php
declare(strict_types=1);
namespace Vendor\OrderFlow\Setup\Patch\Data;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Sales\Model\Order;
use Magento\Sales\Model\Order\StatusFactory;
use Magento\Sales\Model\ResourceModel\Order\Status as StatusResource;
class AddProductionStatuses implements DataPatchInterface
{
private const STATUSES = [
'awaiting_stock' => ['label' => 'Awaiting Stock', 'state' => Order::STATE_PROCESSING],
'in_production' => ['label' => 'In Production', 'state' => Order::STATE_PROCESSING],
];
public function __construct(
private readonly StatusFactory $statusFactory,
private readonly StatusResource $statusResource
) {
}
public function apply(): self
{
foreach (self::STATUSES as $code => $config) {
$status = $this->statusFactory->create();
$this->statusResource->load($status, $code);
$status->setData('status', $code);
$status->setData('label', $config['label']);
$this->statusResource->save($status);
$status->assignState($config['state'], false, true);
}
return $this;
}
public static function getDependencies(): array
{
return [];
}
public function getAliases(): array
{
return [];
}
}
O assignState() recebe o state, se o status é o padrão daquele state e se ele é visível na loja. Mantenha is_default como false a menos que você realmente queira que todo novo pedido em processing receba o seu status. Mudar o status padrão de processing afeta métodos de pagamento, invoices e extensões de terceiros que esperam o rótulo do core.
Mudando o status pelo código
Quando o código move um pedido para um status customizado, defina state e status explicitamente, adicione um comentário no histórico e salve pelo repository:
<?php
declare(strict_types=1);
namespace Vendor\OrderFlow\Model;
use Magento\Framework\Exception\LocalizedException;
use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Model\Order;
class MoveToProduction
{
public function __construct(
private readonly OrderRepositoryInterface $orderRepository
) {
}
public function execute(int $orderId, string $comment = ''): void
{
/** @var Order $order */
$order = $this->orderRepository->get($orderId);
if ($order->getState() !== Order::STATE_PROCESSING) {
throw new LocalizedException(__('Only processing orders can move to production.'));
}
$order->setStatus('in_production');
$order->addCommentToStatusHistory($comment ?: (string) __('Order sent to production.'), 'in_production');
$this->orderRepository->save($order);
}
}
Checar o state primeiro importa. Mover um pedido cancelado ou complete para "em produção" cria um rótulo que contradiz o que o pedido realmente pode fazer. Coloque essa lógica em uma classe de service como a acima e chame de onde vier o gatilho: um botão no admin, um comando CLI, um webhook do ERP ou um queue consumer. Um lugar para mudar a regra, um lugar para testar.
Por que o Magento sobrescreve o seu status
Essa é a parte que surpreende as pessoas. Quando um pedido é salvo depois de uma invoice ou de um shipment, o Magento\Sales\Model\ResourceModel\Order\Handler\State verifica se o pedido deve mudar de state. Por exemplo, um pedido em processing totalmente faturado e totalmente enviado vai para complete, e o status muda para o status padrão de complete.
Esse é o comportamento correto, mas significa que:
- Um status customizado sob
processingé substituído quando o pedido é enviado e faturado. Planeje isso. Normalmente é o que você quer. - Se você precisa de um status customizado depois do envio, como "entregue", atribua-o a
completee defina-o depois que o Magento tiver movido o pedido para esse state. - Quando uma invoice é criada para um pedido pendente, o Magento o move para
processingcom o status padrão, substituindo um status pendente customizado.
Não lute contra isso com plugins que bloqueiam mudanças de state. Isso quebra relatórios, reembolsos e integrações que dependem do fluxo padrão.
Métodos de pagamento e o status inicial
A maioria dos métodos de pagamento tem uma configuração "New Order Status", o valor de config order_status em payment/<code>. O dropdown do admin só lista statuses atribuídos ao state que o método usa, e é por isso que às vezes um status novo "não aparece" ali. Atribua-o ao state certo e ele aparece.
Avisando o cliente
Mudar um status não envia nenhum e-mail sozinho. Para avisar o cliente com o comentário do histórico, use o Magento\Sales\Model\Order\Email\Sender\OrderCommentSender:
$this->orderCommentSender->send($order, true, $comment);
Ele usa o email template "Order Update", que você pode customizar por store. Marque a entrada do histórico como cliente notificado para o admin mostrar que um e-mail foi enviado. Para mensagens mais elaboradas por status, crie um template dedicado. Email templates customizados mostra como. A flag "Visible on Storefront" decide se o rótulo do status e o comentário aparecem na página de histórico de pedidos do cliente.
Transições automáticas
Algumas transições devem acontecer sem ninguém clicar. "Aguardando estoque" pode virar "pronto para enviar" quando chega uma atualização de estoque, ou um pedido parado em um status por tempo demais pode ser sinalizado. Dois padrões funcionam bem:
- Orientado a eventos. Reaja ao que mudou, como um import de estoque ou um webhook, e chame a classe de service para os pedidos afetados. É preciso e rápido.
- Agendado. Um job de cron que busca pedidos por status e idade, usando
OrderRepositoryInterface::getList()com um search criteria emstatuseupdated_at. Mantenha o tamanho da página pequeno e o job idempotente, para que uma segunda execução não faça nada nocivo.
Seja o que for que mova o pedido, sempre deixe um comentário no histórico explicando o motivo. Seis meses depois, o comentário é o único registro de quem ou o que mudou o status. O guia de cron jobs cobre agendamento e locking.
Integrações
Sistemas externos podem adicionar um comentário e mudar o status por REST com POST /V1/orders/{id}/comments, passando um objeto statusHistory com comment, status e is_customer_notified. Para qualquer coisa com regras, prefiro expor o seu próprio endpoint que chama a classe de service acima, para a checagem de state rodar não importa quem envie a request. Se um ERP está conduzindo o workflow, veja padrões de integração com ERP.
FAQ
Qual a diferença entre order state e order status no Magento 2?
O state é um valor fixo no código que decide o que pode ser feito com o pedido. O status é um rótulo configurável atribuído a um state. Muitos statuses podem compartilhar um state.
Posso criar um novo order state?
Não de um jeito prático. Os states são fixos no código do módulo de sales. Crie um status e atribua ao state existente mais próximo.
Por que meu status customizado voltou para Processing ou Complete?
O Magento recalcula o state quando invoices e shipments são salvos e depois aplica o status padrão do novo state. Defina o seu status depois dessa transição, ou atribua-o ao state em que o pedido termina.
Mudar o status envia e-mail para o cliente?
Não. Envie o e-mail de comentário do pedido com o OrderCommentSender, ou com o seu próprio template, quando o status mudar.
Atendo como freelancer em Magento e Adobe Commerce, incluindo workflows de pedido e integrações. Se você precisa de ajuda com isso, fale comigo.
Artigos relacionados
Exportar pedidos do Magento 2 para o ERP sem perder nenhum
Um jeito confiável de exportar pedidos do Magento 2 para um ERP: colunas de status, o evento certo, message queue, retries, idempotência e um cron de segurança.
Declarative schema e data patches no Magento 2, com segurança
Altere o banco do Magento 2 sem quebrar a produção: db_schema.xml, whitelist, data e schema patches, patches revertable e o modo dry-run.
Shipping method customizado no Magento 2: crie um módulo de carrier
Como criar um shipping method customizado no Magento 2.4: config.xml, system.xml, um carrier model com collectRates, regras de frete grátis e APIs externas de tarifa.
Checklist de limpeza de malware no Magento 2 para lojistas
Checklist defensivo para uma loja Magento 2 hackeada: sinais de comprometimento, achar código injetado em arquivos e banco, rebuild limpo, novas credenciais e hardening.