{
  "cells": [
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "# import piplite\n# await piplite.install('python-statemachine[diagrams]')\n# import patch_repr_svg"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "\n# Order control machine (rich model)\n\nAn StateMachine that demonstrates `Actions` being used on a rich model.\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "from statemachine import State\nfrom statemachine import StateMachine\nfrom statemachine.exceptions import AttrNotFound\n\n\nclass Order(object):\n    def __init__(self):\n        self.order_total = 0\n        self.payments = []\n        self.payment_received = False\n\n    def payments_enough(self, amount):\n        return sum(self.payments) + amount >= self.order_total\n\n    def add_to_order(self, amount):\n        self.order_total += amount\n        return self.order_total\n\n    def on_receive_payment(self, amount):\n        self.payments.append(amount)\n        return self.payments\n\n    def after_receive_payment(self):\n        self.payment_received = True\n\n    def wait_for_payment(self):\n        self.payment_received = False\n\n\nclass OrderControl(StateMachine):\n    waiting_for_payment = State(\n        \"Waiting for payment\", initial=True, enter=\"wait_for_payment\"\n    )\n    processing = State(\"Processing\")\n    shipping = State(\"Shipping\")\n    completed = State(\"Completed\", final=True)\n\n    add_to_order = waiting_for_payment.to(waiting_for_payment, before=\"add_to_order\")\n    receive_payment = waiting_for_payment.to(\n        processing, cond=\"payments_enough\"\n    ) | waiting_for_payment.to(waiting_for_payment, unless=\"payments_enough\")\n    process_order = processing.to(shipping, cond=\"payment_received\")\n    ship_order = shipping.to(completed)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "## Testing\n\nLet's first try to create a statemachine instance, using the default dummy model that don't have\nthe needed methods to complete the state machine. Since the required methods will not be found\neither in the state machine or in the model, an exception ``AttrNotFound`` will be raised.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "try:\n    control = OrderControl()\nexcept AttrNotFound as e:\n    assert str(e) == \"Did not found name 'wait_for_payment' from model or statemachine\""
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now initializing with a proper ``order`` instance.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "order = Order()\ncontrol = OrderControl(order)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Send events to add to order\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assert control.send(\"add_to_order\", 3) == 3\nassert control.send(\"add_to_order\", 7) == 10"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Receive a payment of $4...\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control.send(\"receive_payment\", 4)"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Since there's still $6 left to fulfill the payment, we cannot process the order.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "try:\n    control.send(\"process_order\")\nexcept StateMachine.TransitionNotAllowed as err:\n    print(err)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Now paying the left amount, we can proceed.\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control.send(\"receive_payment\", 6)"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control.send(\"process_order\")"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control.send(\"ship_order\")"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {},
      "source": [
        "Just checking the final expected state\n\n"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "order.order_total"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "order.payments"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control.completed.is_active"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "control"
      ]
    },
    {
      "cell_type": "code",
      "execution_count": null,
      "metadata": {
        "collapsed": false
      },
      "outputs": [],
      "source": [
        "assert order.order_total == 10\nassert order.payments == [4, 6]\nassert control.completed.is_active"
      ]
    }
  ],
  "metadata": {
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.8.1"
    }
  },
  "nbformat": 4,
  "nbformat_minor": 0
}