{"record":{"id":"28788ef2d7e5a847","repo":"phacility/phabricator","slug":"this-order-can-not-be-voided-because-it-is-not-an","errorCode":null,"errorMessage":"This order can not be voided because it is not an invoice.","messagePattern":"This order can not be voided because it is not an invoice\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"warning","filePath":"src/applications/phortune/storage/PhortuneCart.php","lineNumber":545,"sourceCode":"  public function assertCanRefundOrder() {\n    switch ($this->getStatus()) {\n      case self::STATUS_BUILDING:\n        throw new Exception(\n          pht(\n            'This order can not be refunded because the application has not '.\n            'finished building it yet.'));\n      case self::STATUS_READY:\n        throw new Exception(\n          pht(\n            'This order can not be refunded because it has not been placed.'));\n    }\n\n    return $this->getImplementation()->assertCanRefundOrder($this);\n  }\n\n  public function assertCanVoidOrder() {\n    if (!$this->getIsInvoice()) {\n      throw new Exception(\n        pht(\n          'This order can not be voided because it is not an invoice.'));\n    }\n\n    switch ($this->getStatus()) {\n      case self::STATUS_READY:\n        break;\n      default:\n        throw new Exception(\n          pht(\n            'This order can not be voided because it is not ready for '.\n            'payment.'));\n    }\n\n    return null;\n  }\n\n","sourceCodeStart":527,"sourceCodeEnd":563,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/phortune/storage/PhortuneCart.php#L527-L563","documentation":"assertCanVoidOrder() throws when getIsInvoice() is false: voiding is reserved for subscription-generated invoices, because a void marks an issued invoice as never-payable. Ordinary orders do not go through voiding - they are cancelled (before purchase) or refunded (after charge).","triggerScenarios":"Calling assertCanVoidOrder() on a cart created by a normal purchase flow (isInvoice is 0/absent), e.g. pointing a void action at a user-initiated order instead of a subscription invoice.","commonSituations":"Generic order-management tooling applying the void action to all carts; confusion between void (invoice-only), cancel, and refund in custom controllers.","solutions":["Check $cart->getIsInvoice() before offering the void action.","For non-invoice orders use cancel (unplaced) or refund (charged) instead.","Filter lists so void only appears on subscription invoice carts."],"exampleFix":"// before\n$cart->assertCanVoidOrder(); // throws 'This order can not be voided because it is not an invoice.'\n\n// after\nif ($cart->getIsInvoice()) {\n  $cart->assertCanVoidOrder();\n  // proceed with void\n} else {\n  // use canCancelOrder()/canRefundOrder() paths for normal orders\n}","handlingStrategy":"validation","validationCode":"if (!$cart->getIsInvoice()) {\n  // void applies only to subscription invoices; use cancel or refund instead\n  return $this->newDialog()->setTitle(pht('Not an Invoice'));\n}","typeGuard":"function isInvoiceCart(PhortuneCart $cart) {\n  return (bool)$cart->getIsInvoice();\n}","tryCatchPattern":null,"preventionTips":["Offer void only on carts with getIsInvoice() true.","Map intents correctly: cancel before purchase, refund after charge, void only for invoices.","Keep void actions out of generic order tooling."],"tags":["phortune","phabricator","php","payments","cart","invoice","order-lifecycle"],"backgroundTag":"invalid-cart-status","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}