{"action":"created","issue":{"url":"https://api.github.com/repos/Sylius/Sylius/issues/4060","repository_url":"https://api.github.com/repos/Sylius/Sylius","labels_url":"https://api.github.com/repos/Sylius/Sylius/issues/4060/labels{/name}","comments_url":"https://api.github.com/repos/Sylius/Sylius/issues/4060/comments","events_url":"https://api.github.com/repos/Sylius/Sylius/issues/4060/events","html_url":"https://github.com/Sylius/Sylius/issues/4060","id":131502485,"number":4060,"title":"[RFC] Order state machine","user":{"login":"pjedrzejewski","id":614970,"avatar_url":"https://avatars.githubusercontent.com/u/614970?v=3","gravatar_id":"","url":"https://api.github.com/users/pjedrzejewski","html_url":"https://github.com/pjedrzejewski","followers_url":"https://api.github.com/users/pjedrzejewski/followers","following_url":"https://api.github.com/users/pjedrzejewski/following{/other_user}","gists_url":"https://api.github.com/users/pjedrzejewski/gists{/gist_id}","starred_url":"https://api.github.com/users/pjedrzejewski/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pjedrzejewski/subscriptions","organizations_url":"https://api.github.com/users/pjedrzejewski/orgs","repos_url":"https://api.github.com/users/pjedrzejewski/repos","events_url":"https://api.github.com/users/pjedrzejewski/events{/privacy}","received_events_url":"https://api.github.com/users/pjedrzejewski/received_events","type":"User","site_admin":false},"labels":[{"url":"https://api.github.com/repos/Sylius/Sylius/labels/RFC","name":"RFC","color":"207de5"}],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":12,"created_at":"2016-02-04T23:11:51Z","updated_at":"2016-02-12T15:37:35Z","closed_at":null,"body":"Hello!\r\n\r\nCurrent states and state machine of Order is pretty confusing. This RFC aims to solve following problems:\r\n\r\n* Clarifying all states that we have on the order;\r\n* Making the checkout process cleaner;\r\n* Making it easier to identify what is cart and what is order;\r\n\r\nI will be talking about the following states and field:\r\n\r\n* Order.state\r\n* Order.checkoutState\r\n* Order.shippingState\r\n* Order.paymentState\r\n* Order.completedAt\r\n\r\nWe currently use ``Order.completedAt`` to decide whether it is a Cart or Order. That's annoying. As suggested in another PR/issue, I think we should rename it to ``orderCheckoutCompletedAt`` and use it for information purposes when exactly the checkout has been completed.\r\n\r\n## Checkout state\r\n\r\nNow, to the states. Let's start from ``checkoutState``, which is pretty clear and reworked already in #3918. I will paste the configuration overview for reference:\r\n\r\n```yaml\r\nwinzou_state_machine:\r\n    sylius_order_checkout:\r\n        class: %sylius.model.order.class%\r\n        property_path: checkoutState\r\n        graph: sylius_order_checkout\r\n        state_machine_class: %sylius.state_machine.class%\r\n        states:\r\n            cart: ~\r\n            started: ~\r\n            addressed: ~\r\n            shipping_selected: ~\r\n            payment_selected: ~\r\n            completed: ~\r\n        transitions:\r\n            start:\r\n                from: [cart]\r\n                to: started\r\n            address:\r\n                from: [started]\r\n                to: addressed\r\n            readdress:\r\n                from: [payment_selected, shipping_selected, addressed]\r\n                to: started\r\n            select_shipping:\r\n                from: [addressed]\r\n                to: shipping_selected\r\n            reselect_shipping:\r\n                from: [payment_selected, shipping_selected]\r\n                to: addressed\r\n            select_payment:\r\n                from: [shipping_selected]\r\n                to: payment_selected\r\n            reselect_payment:\r\n                from: [payment_selected]\r\n                to: shipping_selected\r\n            complete:\r\n                from: [payment_selected]\r\n                to: completed\r\n```\r\n\r\nThis is pretty much self-explanatory and covers the whole checkout process. If you customize it, you will need to rework this state-machine, but this gives us clean way of executing callbacks, recalculations, etc.\r\n\r\n## State\r\n\r\nNext state is ``Order.state``, this one is a bit tricky, but it should represent the overall state of the order and also we should use it to identify if Order is a cart. Here is the state machine I propose as initial implementation:\r\n\r\n```yaml\r\nwinzou_state_machine:\r\n    sylius_order:\r\n        class: %sylius.model.order.class%\r\n        property_path: state\r\n        graph: sylius_order\r\n        state_machine_class: %sylius.state_machine.class%\r\n        states:\r\n            cart: ~\r\n            new: ~\r\n            abandoned: ~\r\n            cancelled: ~\r\n            closed: ~\r\n        transitions:\r\n            create:\r\n                from: [cart]\r\n                to: new \r\n            abandon:\r\n                from: [new]\r\n                to: abandoned\r\n            cancel:\r\n                from: [new]\r\n                to: cancelled\r\n            retry:\r\n                from: [cancelled, abandoned]\r\n                to: new\r\n            close:\r\n                from: [new]\r\n                to: closed\r\n```\r\n\r\nExplanation of the states:\r\n\r\n* ``cart`` - This is default state of the order and we use it to differentiate between Orders and Carts. Basically, everything that is not in state ``cart`` is an Order that should show up in the admin.\r\n* ``new`` - Order goes into this state as soon as the checkout is completed. \"New\" orders appears in the backend.\r\n* ``abandoned`` - If someone completes the checkout, but does not pay after X time, order goes into ``abandoned`` state, inventory is released, etc.\r\n* ``cancelled`` - Cancelled via admin.\r\n* ``closed`` - Final state of the order. When the order is paid and shipped, it becomes \"closed\". You can't modify it and that's end of the story.\r\n\r\nTransitions are self-explanatory. Later we want to give the ability to \"retry\" an abandoned/cancelled order, that's why I added this transition.\r\n\r\n## Payment state\r\n\r\nThis one represents the status of all payments on the order. We already partially support multiple payments per order, that's why I want to have this separate state.\r\n\r\n```yaml\r\nwinzou_state_machine:\r\n    sylius_order_shipping:\r\n        class: %sylius.model.order.class%\r\n        property_path: paymentState\r\n        graph: sylius_order_payment\r\n        state_machine_class: %sylius.state_machine.class%\r\n        states:\r\n            cart: ~\r\n            awaiting_payment: ~\r\n            partially_paid: ~\r\n            cancelled: ~\r\n            paid: ~\r\n        transitions:\r\n            request:\r\n                from: [cart]\r\n                to: awaiting_payment\r\n            partially_pay:\r\n                from: [awaiting_payment]\r\n                to: partially_paid\r\n            cancel:\r\n                from: [awaiting_payment]\r\n                to: cancelled\r\n            pay:\r\n                from: [awaiting_payment, partially_paid]\r\n                to: paid\r\n```\r\n\r\nThis one is very rough, I am sure we will need more transitions or even maybe states. I am unsure about the ``partially_paid`` state, perhaps we should have just one ``amount_due`` state or something like that.\r\nOne thing I want to avoid is discussing these longer than until next weekend because we really need to clean this up and get rid of remaining event listeners to regain control of the whole order processing.\r\n\r\n## Shipping state\r\n\r\nSylius Order can have multiple shipments too, so we need to know what is the overall status of the order.\r\n\r\n```yaml\r\nwinzou_state_machine:\r\n    sylius_order_shipping:\r\n        class: %sylius.model.order.class%\r\n        property_path: shippingState \r\n        graph: sylius_order_shipping\r\n        state_machine_class: %sylius.state_machine.class%\r\n        states:\r\n            cart: ~\r\n            abandoned: ~\r\n            new: ~\r\n            ready: ~\r\n            cancelled: ~\r\n            partially_shipped: ~\r\n            shipped: ~\r\n        transitions:\r\n            request:\r\n                from: [cart]\r\n                to: new\r\n            abandon:\r\n                from: [new]\r\n                to: abandoned\r\n            prepare:\r\n                from: [new, pending]\r\n                to: ready\r\n            cancel:\r\n                from: [new, pending]\r\n                to: cancelled\r\n            partially_ship:\r\n                from: [ready, pending]\r\n                to: partially_shipped\r\n            ship:\r\n                from: [ready, partially_shipped]\r\n                to: shipped\r\n```\r\n\r\nI am not entirely sure about this one, so just throwing it here. I think we could safely skip ``partially_shipped`` state and transitions for now, because Sylius does not handle multiple shipments now. Except in the checkout.\r\n\r\n## Summary\r\n\r\nSome of these are already there, but not properly used or not in use at all. Let's brainstorm a bit and finally put this state machine into action!\r\n"},"comment":{"url":"https://api.github.com/repos/Sylius/Sylius/issues/comments/183377180","html_url":"https://github.com/Sylius/Sylius/issues/4060#issuecomment-183377180","issue_url":"https://api.github.com/repos/Sylius/Sylius/issues/4060","id":183377180,"user":{"login":"gabiudrescu","id":5156054,"avatar_url":"https://avatars.githubusercontent.com/u/5156054?v=3","gravatar_id":"","url":"https://api.github.com/users/gabiudrescu","html_url":"https://github.com/gabiudrescu","followers_url":"https://api.github.com/users/gabiudrescu/followers","following_url":"https://api.github.com/users/gabiudrescu/following{/other_user}","gists_url":"https://api.github.com/users/gabiudrescu/gists{/gist_id}","starred_url":"https://api.github.com/users/gabiudrescu/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gabiudrescu/subscriptions","organizations_url":"https://api.github.com/users/gabiudrescu/orgs","repos_url":"https://api.github.com/users/gabiudrescu/repos","events_url":"https://api.github.com/users/gabiudrescu/events{/privacy}","received_events_url":"https://api.github.com/users/gabiudrescu/received_events","type":"User","site_admin":false},"created_at":"2016-02-12T15:37:35Z","updated_at":"2016-02-12T15:37:35Z","body":"well, my description above is the \"desired\" implementation, not the current one. I'll go into specifics in private, if you want to know more. \r\n\r\nAnyway, I got what I wanted from the description you mentioned above regarding fraud. In our case, \"fraud\" review its optional if the user doesn't have automated validation credentials. In this case, an operator is going to call him and ask for additional papers.\r\n\r\notherwise, his order will be shipped without further questions."}}