{
  "id": "P146",
  "slug": "order-states",
  "key": "P146-order-states",
  "title": "Order states",
  "summary": "Orders kept as three fields - payment, fulfilment, called off - and as one status column of five values for eighteen real states: what the column reads back as, and the answers it gets wrong.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P146 - Order states\n\nOrders kept two ways - as three fields, and as one status column - with\nevery event applied to both, what the column reads back as, and the\nanswers each model gives.\n\n- `main.eml` - the menu, the orders in both models, an event, the reports,\n  every real state, and a new order\n- `orders.eml` - the fields and the column's values, when an event can\n  happen, the event applied to the fields and to the column, the value that\n  names a state, the state a value claims, and the three questions\n\nHow each part works:\n\n- An order's state is three facts: payment (unpaid, paid, refunded),\n  fulfilment (none, shipped, returned) and whether it was called off. As in\n  the corpus case `status-field-conflation`, the column has five values -\n  pending, paid, shipped, cancelled, refunded - for eighteen real states,\n  and only five of those have a value; a new order can reach twelve of\n  them.\n- Each event handler writes the value nearest to what happened, and a\n  return has no value at all, so it leaves the column as it was. Read back,\n  the column gives a state that did not happen: refunded after shipping\n  reads as never shipped, cancelled after paying reads as unpaid.\n- The column can be wrong even where a value exists: an order shipped\n  before it is paid ends as paid and shipped, which `shipped` names, but the\n  payment handler wrote `paid` over `shipped`.\n- The questions: the money we hold, the parcels with the customer, and the\n  refunds we owe. The column never shows a refund owed; for the opening\n  orders it answers all three wrong, and nothing raises an error.\n\nWhat is checked: menu choices 1 to 6; an order number that exists and one\nof the five events, and only when the event can happen to that order. An\nempty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the orders, the reports and every state;\n- a new order shipped, paid and refunded; order 3 returned; the orders and\n  the reports again.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- order numbers: a cancel, x, 1234 and 9; events: a cancel and fly;\n- events that cannot happen: paying a paid order, returning one never\n  shipped, shipping a cancelled one, returning one returned, cancelling a\n  shipped one and a cancelled one, refunding an unpaid one;\n- order 1 cancelled and order 6 refunded; the orders and the reports.\n\nBuilt on the verified corpus case `status-field-conflation` (payment and\nfulfilment as a pair in one column, the states with no value, and an order\nhistory the column cannot hold).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P146 order states: orders kept as three fields and as one status column,\n# events applied to both, what the column reads back as, the reports each\n# model gives, and the real states the column can name.\nimport orders\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef pad(s, n):\n    s => out\n    while len(out) < n:\n        out + \" \" => out\n    return out\n\ndef lpad(s, n):\n    s => out\n    while len(out) < n:\n        \" \" + out => out\n    return out\n\ndef triple(st):\n    return st[0] + \", \" + st[1] + \", \" + st[2]\n\ndef wrong(st, col):\n    # the fields the column reads back wrong, or \"\"\n    orders.read(col) => back\n    [\"payment\", \"fulfilment\", \"called off\"] => names\n    \"\" => out\n    0 => i\n    while i < 3:\n        if back[i] != st[i]:\n            if out == \"\":\n                names[i] => out\n            else:\n                out + \" and \" + names[i] => out\n        i + 1 => i\n    return out\n\ndef make(number, evs):\n    orders.start() => st\n    \"pending\" => col\n    for e in evs:\n        orders.apply(st, e) => st\n        orders.write(col, e) => col\n    return [number, st, col]\n\ndef show(os):\n    (\"  \" + pad(\"order\", 7) + pad(\"three fields\", 30) + pad(\"column\", 11) + \"reads back wrong\") ^0\n    for o in os:\n        wrong(o[1], o[2]) => w\n        if w == \"\":\n            \"-\" => w\n        (\"  \" + pad(\"#\" + str(o[0]), 7) + pad(triple(o[1]), 30) + pad(o[2], 11) + w) ^0\n\ndef find(os, n):\n    0 => i\n    for o in os:\n        if o[0] == n:\n            return i\n        i + 1 => i\n    return 0 - 1\n\ndef event(os):\n    plain(input(\"order number> \")) => t\n    if t == \"\":\n        \"Cancelled.\" ^0\n        return os\n    if len(t) > 3:\n        \"Type an order number.\" ^0\n        return os\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            \"Type an order number.\" ^0\n            return os\n    find(os, int(t)) => k\n    if k < 0:\n        (\"There is no order #\" + t + \".\") ^0\n        return os\n    plain(input(\"event (pay, ship, refund, return, cancel)> \")) => e\n    if e == \"\":\n        \"Cancelled.\" ^0\n        return os\n    False => known\n    for x in orders.events():\n        if x == e:\n            True => known\n    if not known:\n        \"Type pay, ship, refund, return or cancel.\" ^0\n        return os\n    os[k] => o\n    orders.problem(o[1], e) => why\n    if why != \"\":\n        (\"Order #\" + str(o[0]) + \" cannot \" + e + \": \" + why + \".\") ^0\n        return os\n    orders.apply(o[1], e) => st\n    orders.write(o[2], e) => col\n    (\"Order #\" + str(o[0]) + \", \" + e + \": three fields \" + triple(st) + \"; the column \" + o[2] + \" -> \" + col + \".\") ^0\n    orders.name_of(st) => exact\n    wrong(st, col) => w\n    if w == \"\":\n        (\"The column names this state exactly: \" + col + \".\") ^0\n    elif exact == \"\":\n        (\"No value names \" + triple(st) + \"; the column reads back as \" + triple(orders.read(col)) + \", wrong on \" + w + \".\") ^0\n    else:\n        (\"The column reads back as \" + triple(orders.read(col)) + \", wrong on \" + w + \", though \" + exact + \" would name it.\") ^0\n    [] => out\n    0 => i\n    for x in os:\n        if i == k:\n            out + [[o[0], st, col]] => out\n        else:\n            out + [x] => out\n        i + 1 => i\n    return out\n\ndef numbers(os, which, from_column):\n    \"\" => out\n    0 => n\n    for o in os:\n        if from_column:\n            orders.read(o[2]) => st\n        else:\n            o[1] => st\n        if which == 0:\n            orders.money_held(st) => yes\n        elif which == 1:\n            orders.parcel_out(st) => yes\n        else:\n            orders.refund_owed(st) => yes\n        if yes:\n            n + 1 => n\n            if out == \"\":\n                \"#\" + str(o[0]) => out\n            else:\n                out + \" #\" + str(o[0]) => out\n    if out == \"\":\n        \"none\" => out\n    return [n, out]\n\ndef reports(os):\n    [\"money we hold\", \"parcels with the customer\", \"refunds we owe\"] => qs\n    (\"  \" + pad(\"question\", 28) + pad(\"three fields\", 22) + \"the column\") ^0\n    0 => differ\n    0 => i\n    while i < 3:\n        numbers(os, i, False) => a\n        numbers(os, i, True) => b\n        (\"  \" + pad(qs[i], 28) + pad(str(a[0]) + \": \" + a[1], 22) + str(b[0]) + \": \" + b[1]) ^0\n        if a[1] != b[1]:\n            differ + 1 => differ\n        i + 1 => i\n    if differ == 0:\n        \"Both models answer every question the same, for these orders.\" ^0\n    else:\n        (\"The column answers \" + str(differ) + \" of the 3 questions wrong, and raises no error doing it.\") ^0\n\ndef space():\n    \"Every real state, and the value that names it:\" ^0\n    0 => named\n    0 => total\n    for c in [\"open\", \"cancelled\"]:\n        for p in orders.payments():\n            for f in orders.fulfilments():\n                [p, f, c] => st\n                orders.name_of(st) => v\n                total + 1 => total\n                if v == \"\":\n                    \"-\" => v\n                else:\n                    named + 1 => named\n                (\"  \" + pad(triple(st), 30) + v) ^0\n    (str(len(orders.columns())) + \" values name \" + str(named) + \" of the \" + str(total) + \" real states; \" + str(total - named) + \" have no value, and the code that meets one writes a neighbour.\") ^0\n\n[] => os\nfor o in orders.opening():\n    os + [make(o[0], o[1])] => os\n\n\"== Order states ==\" ^0\n\"Payment, fulfilment and calling off, kept as three fields and as one status column.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        show(os)\n    elif choice == \"2\":\n        event(os) => os\n    elif choice == \"3\":\n        reports(os)\n    elif choice == \"4\":\n        space()\n    elif choice == \"5\":\n        os[len(os) - 1][0] + 1 => n\n        os + [make(n, [])] => os\n        (\"Order #\" + str(n) + \" placed: unpaid, none, open; the column pending.\") ^0\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import orders\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef pad(s, n):\n    out = s\n    while len(out) < n:\n        out = out + \" \"\n    return out\n\ndef lpad(s, n):\n    out = s\n    while len(out) < n:\n        out = \" \" + out\n    return out\n\ndef triple(st):\n    return st[0] + \", \" + st[1] + \", \" + st[2]\n\ndef wrong(st, col):\n    back = orders.read(col)\n    names = [\"payment\", \"fulfilment\", \"called off\"]\n    out = \"\"\n    i = 0\n    while i < 3:\n        if back[i] != st[i]:\n            if out == \"\":\n                out = names[i]\n            else:\n                out = out + \" and \" + names[i]\n        i = i + 1\n    return out\n\ndef make(number, evs):\n    st = orders.start()\n    col = \"pending\"\n    for e in evs:\n        st = orders.apply(st, e)\n        col = orders.write(col, e)\n    return [number, st, col]\n\ndef show(os):\n    print(\"  \" + pad(\"order\", 7) + pad(\"three fields\", 30) + pad(\"column\", 11) + \"reads back wrong\")\n    for o in os:\n        w = wrong(o[1], o[2])\n        if w == \"\":\n            w = \"-\"\n        print(\"  \" + pad(\"#\" + str(o[0]), 7) + pad(triple(o[1]), 30) + pad(o[2], 11) + w)\n\ndef find(os, n):\n    i = 0\n    for o in os:\n        if o[0] == n:\n            return i\n        i = i + 1\n    return 0 - 1\n\ndef event(os):\n    t = plain(input(\"order number> \"))\n    if t == \"\":\n        print(\"Cancelled.\")\n        return os\n    if len(t) > 3:\n        print(\"Type an order number.\")\n        return os\n    for c in t:\n        if c < \"0\" or c > \"9\":\n            print(\"Type an order number.\")\n            return os\n    k = find(os, int(t))\n    if k < 0:\n        print(\"There is no order #\" + t + \".\")\n        return os\n    e = plain(input(\"event (pay, ship, refund, return, cancel)> \"))\n    if e == \"\":\n        print(\"Cancelled.\")\n        return os\n    known = False\n    for x in orders.events():\n        if x == e:\n            known = True\n    if not known:\n        print(\"Type pay, ship, refund, return or cancel.\")\n        return os\n    o = os[k]\n    why = orders.problem(o[1], e)\n    if why != \"\":\n        print(\"Order #\" + str(o[0]) + \" cannot \" + e + \": \" + why + \".\")\n        return os\n    st = orders.apply(o[1], e)\n    col = orders.write(o[2], e)\n    print(\"Order #\" + str(o[0]) + \", \" + e + \": three fields \" + triple(st) + \"; the column \" + o[2] + \" -> \" + col + \".\")\n    exact = orders.name_of(st)\n    w = wrong(st, col)\n    if w == \"\":\n        print(\"The column names this state exactly: \" + col + \".\")\n    elif exact == \"\":\n        print(\"No value names \" + triple(st) + \"; the column reads back as \" + triple(orders.read(col)) + \", wrong on \" + w + \".\")\n    else:\n        print(\"The column reads back as \" + triple(orders.read(col)) + \", wrong on \" + w + \", though \" + exact + \" would name it.\")\n    out = []\n    i = 0\n    for x in os:\n        if i == k:\n            out = out + [[o[0], st, col]]\n        else:\n            out = out + [x]\n        i = i + 1\n    return out\n\ndef numbers(os, which, from_column):\n    out = \"\"\n    n = 0\n    for o in os:\n        if from_column:\n            st = orders.read(o[2])\n        else:\n            st = o[1]\n        if which == 0:\n            yes = orders.money_held(st)\n        elif which == 1:\n            yes = orders.parcel_out(st)\n        else:\n            yes = orders.refund_owed(st)\n        if yes:\n            n = n + 1\n            if out == \"\":\n                out = \"#\" + str(o[0])\n            else:\n                out = out + \" #\" + str(o[0])\n    if out == \"\":\n        out = \"none\"\n    return [n, out]\n\ndef reports(os):\n    qs = [\"money we hold\", \"parcels with the customer\", \"refunds we owe\"]\n    print(\"  \" + pad(\"question\", 28) + pad(\"three fields\", 22) + \"the column\")\n    differ = 0\n    i = 0\n    while i < 3:\n        a = numbers(os, i, False)\n        b = numbers(os, i, True)\n        print(\"  \" + pad(qs[i], 28) + pad(str(a[0]) + \": \" + a[1], 22) + str(b[0]) + \": \" + b[1])\n        if a[1] != b[1]:\n            differ = differ + 1\n        i = i + 1\n    if differ == 0:\n        print(\"Both models answer every question the same, for these orders.\")\n    else:\n        print(\"The column answers \" + str(differ) + \" of the 3 questions wrong, and raises no error doing it.\")\n\ndef space():\n    print(\"Every real state, and the value that names it:\")\n    named = 0\n    total = 0\n    for c in [\"open\", \"cancelled\"]:\n        for p in orders.payments():\n            for f in orders.fulfilments():\n                st = [p, f, c]\n                v = orders.name_of(st)\n                total = total + 1\n                if v == \"\":\n                    v = \"-\"\n                else:\n                    named = named + 1\n                print(\"  \" + pad(triple(st), 30) + v)\n    print(str(len(orders.columns())) + \" values name \" + str(named) + \" of the \" + str(total) + \" real states; \" + str(total - named) + \" have no value, and the code that meets one writes a neighbour.\")\n\nos = []\nfor o in orders.opening():\n    os = os + [make(o[0], o[1])]\nprint(\"== Order states ==\")\nprint(\"Payment, fulfilment and calling off, kept as three fields and as one status column.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        show(os)\n    elif choice == \"2\":\n        os = event(os)\n    elif choice == \"3\":\n        reports(os)\n    elif choice == \"4\":\n        space()\n    elif choice == \"5\":\n        n = os[len(os) - 1][0] + 1\n        os = os + [make(n, [])]\n        print(\"Order #\" + str(n) + \" placed: unpaid, none, open; the column pending.\")\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "orders.eml",
      "eml": "# P146 order states - an order's payment, fulfilment and whether it was\n# called off, kept as three fields and as one status column. As in the\n# corpus case status-field-conflation, the column is asked to hold a pair\n# (here a triple) in one slot: five values for eighteen real states. The\n# event handlers write the value nearest to what happened, and reading the\n# column back gives a state that did not happen. A state is [payment,\n# fulfilment, called off]: payment unpaid, paid or refunded; fulfilment\n# none, shipped or returned; called off \"open\" or \"cancelled\".\n\ndef payments():\n    return [\"unpaid\", \"paid\", \"refunded\"]\n\ndef fulfilments():\n    return [\"none\", \"shipped\", \"returned\"]\n\ndef columns():\n    return [\"pending\", \"paid\", \"shipped\", \"cancelled\", \"refunded\"]\n\ndef events():\n    return [\"pay\", \"ship\", \"refund\", \"return\", \"cancel\"]\n\ndef start():\n    return [\"unpaid\", \"none\", \"open\"]\n\ndef problem(st, e):\n    # why event e cannot happen to state st, or \"\" when it can\n    if e == \"pay\":\n        if st[0] != \"unpaid\":\n            return \"it is already \" + st[0]\n        if st[2] == \"cancelled\":\n            return \"it was cancelled\"\n    if e == \"ship\":\n        if st[1] != \"none\":\n            return \"it is already \" + st[1]\n        if st[2] == \"cancelled\":\n            return \"it was cancelled\"\n    if e == \"refund\" and st[0] != \"paid\":\n        return \"it is \" + st[0] + \", not paid\"\n    if e == \"return\" and st[1] != \"shipped\":\n        if st[1] == \"none\":\n            return \"it was never shipped\"\n        return \"it is already returned\"\n    if e == \"cancel\":\n        if st[2] == \"cancelled\":\n            return \"it is already cancelled\"\n        if st[1] != \"none\":\n            return \"it is already \" + st[1]\n    return \"\"\n\ndef apply(st, e):\n    # the three fields after event e\n    if e == \"pay\":\n        return [\"paid\", st[1], st[2]]\n    if e == \"ship\":\n        return [st[0], \"shipped\", st[2]]\n    if e == \"refund\":\n        return [\"refunded\", st[1], st[2]]\n    if e == \"return\":\n        return [st[0], \"returned\", st[2]]\n    return [st[0], st[1], \"cancelled\"]\n\ndef write(col, e):\n    # the column after event e: each handler writes the value nearest to\n    # what happened; a return has no value, so it leaves the column\n    if e == \"pay\":\n        return \"paid\"\n    if e == \"ship\":\n        return \"shipped\"\n    if e == \"refund\":\n        return \"refunded\"\n    if e == \"cancel\":\n        return \"cancelled\"\n    return col\n\ndef name_of(st):\n    # the value that names state st exactly, or \"\"\n    if st[2] == \"open\":\n        if st[0] == \"unpaid\" and st[1] == \"none\":\n            return \"pending\"\n        if st[0] == \"paid\" and st[1] == \"none\":\n            return \"paid\"\n        if st[0] == \"paid\" and st[1] == \"shipped\":\n            return \"shipped\"\n        if st[0] == \"refunded\" and st[1] == \"none\":\n            return \"refunded\"\n    elif st[0] == \"unpaid\" and st[1] == \"none\":\n        return \"cancelled\"\n    return \"\"\n\ndef read(col):\n    # the state the column claims\n    if col == \"paid\":\n        return [\"paid\", \"none\", \"open\"]\n    if col == \"shipped\":\n        return [\"paid\", \"shipped\", \"open\"]\n    if col == \"refunded\":\n        return [\"refunded\", \"none\", \"open\"]\n    if col == \"cancelled\":\n        return [\"unpaid\", \"none\", \"cancelled\"]\n    return [\"unpaid\", \"none\", \"open\"]\n\ndef opening():\n    # [order number, events so far]\n    return [[1, []], [2, [\"pay\"]], [3, [\"pay\", \"ship\"]], [4, [\"pay\", \"ship\", \"refund\"]], [5, [\"pay\", \"ship\", \"return\"]], [6, [\"pay\", \"cancel\"]]]\n\ndef money_held(st):\n    return st[0] == \"paid\"\n\ndef parcel_out(st):\n    return st[1] == \"shipped\"\n\ndef refund_owed(st):\n    return st[0] == \"paid\" and (st[2] == \"cancelled\" or st[1] == \"returned\")\n",
      "python": "def payments():\n    return [\"unpaid\", \"paid\", \"refunded\"]\n\ndef fulfilments():\n    return [\"none\", \"shipped\", \"returned\"]\n\ndef columns():\n    return [\"pending\", \"paid\", \"shipped\", \"cancelled\", \"refunded\"]\n\ndef events():\n    return [\"pay\", \"ship\", \"refund\", \"return\", \"cancel\"]\n\ndef start():\n    return [\"unpaid\", \"none\", \"open\"]\n\ndef problem(st, e):\n    if e == \"pay\":\n        if st[0] != \"unpaid\":\n            return \"it is already \" + st[0]\n        if st[2] == \"cancelled\":\n            return \"it was cancelled\"\n    if e == \"ship\":\n        if st[1] != \"none\":\n            return \"it is already \" + st[1]\n        if st[2] == \"cancelled\":\n            return \"it was cancelled\"\n    if e == \"refund\" and st[0] != \"paid\":\n        return \"it is \" + st[0] + \", not paid\"\n    if e == \"return\" and st[1] != \"shipped\":\n        if st[1] == \"none\":\n            return \"it was never shipped\"\n        return \"it is already returned\"\n    if e == \"cancel\":\n        if st[2] == \"cancelled\":\n            return \"it is already cancelled\"\n        if st[1] != \"none\":\n            return \"it is already \" + st[1]\n    return \"\"\n\ndef apply(st, e):\n    if e == \"pay\":\n        return [\"paid\", st[1], st[2]]\n    if e == \"ship\":\n        return [st[0], \"shipped\", st[2]]\n    if e == \"refund\":\n        return [\"refunded\", st[1], st[2]]\n    if e == \"return\":\n        return [st[0], \"returned\", st[2]]\n    return [st[0], st[1], \"cancelled\"]\n\ndef write(col, e):\n    if e == \"pay\":\n        return \"paid\"\n    if e == \"ship\":\n        return \"shipped\"\n    if e == \"refund\":\n        return \"refunded\"\n    if e == \"cancel\":\n        return \"cancelled\"\n    return col\n\ndef name_of(st):\n    if st[2] == \"open\":\n        if st[0] == \"unpaid\" and st[1] == \"none\":\n            return \"pending\"\n        if st[0] == \"paid\" and st[1] == \"none\":\n            return \"paid\"\n        if st[0] == \"paid\" and st[1] == \"shipped\":\n            return \"shipped\"\n        if st[0] == \"refunded\" and st[1] == \"none\":\n            return \"refunded\"\n    elif st[0] == \"unpaid\" and st[1] == \"none\":\n        return \"cancelled\"\n    return \"\"\n\ndef read(col):\n    if col == \"paid\":\n        return [\"paid\", \"none\", \"open\"]\n    if col == \"shipped\":\n        return [\"paid\", \"shipped\", \"open\"]\n    if col == \"refunded\":\n        return [\"refunded\", \"none\", \"open\"]\n    if col == \"cancelled\":\n        return [\"unpaid\", \"none\", \"cancelled\"]\n    return [\"unpaid\", \"none\", \"open\"]\n\ndef opening():\n    return [[1, []], [2, [\"pay\"]], [3, [\"pay\", \"ship\"]], [4, [\"pay\", \"ship\", \"refund\"]], [5, [\"pay\", \"ship\", \"return\"]], [6, [\"pay\", \"cancel\"]]]\n\ndef money_held(st):\n    return st[0] == \"paid\"\n\ndef parcel_out(st):\n    return st[1] == \"shipped\"\n\ndef refund_owed(st):\n    return st[0] == \"paid\" and (st[2] == \"cancelled\" or st[1] == \"returned\")\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n2\n\n2\nx\n2\n1234\n2\n9\n2\n1\n\n2\n1\nfly\n2\n2\npay\n2\n1\nreturn\n2\n6\nship\n2\n5\nreturn\n2\n3\ncancel\n2\n6\ncancel\n2\n1\nrefund\n2\n1\ncancel\n2\n6\nrefund\n1\n3\n6\n",
      "screen": "== Order states ==\nPayment, fulfilment and calling off, kept as three fields and as one status column.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 0\nPick a number from 1 to 6.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> x\nPick a number from 1 to 6.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> \nCancelled.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> x\nType an order number.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 1234\nType an order number.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 9\nThere is no order #9.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 1\nevent (pay, ship, refund, return, cancel)> \nCancelled.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 1\nevent (pay, ship, refund, return, cancel)> fly\nType pay, ship, refund, return or cancel.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 2\nevent (pay, ship, refund, return, cancel)> pay\nOrder #2 cannot pay: it is already paid.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 1\nevent (pay, ship, refund, return, cancel)> return\nOrder #1 cannot return: it was never shipped.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 6\nevent (pay, ship, refund, return, cancel)> ship\nOrder #6 cannot ship: it was cancelled.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 5\nevent (pay, ship, refund, return, cancel)> return\nOrder #5 cannot return: it is already returned.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 3\nevent (pay, ship, refund, return, cancel)> cancel\nOrder #3 cannot cancel: it is already shipped.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 6\nevent (pay, ship, refund, return, cancel)> cancel\nOrder #6 cannot cancel: it is already cancelled.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 1\nevent (pay, ship, refund, return, cancel)> refund\nOrder #1 cannot refund: it is unpaid, not paid.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 1\nevent (pay, ship, refund, return, cancel)> cancel\nOrder #1, cancel: three fields unpaid, none, cancelled; the column pending -> cancelled.\nThe column names this state exactly: cancelled.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 6\nevent (pay, ship, refund, return, cancel)> refund\nOrder #6, refund: three fields refunded, none, cancelled; the column cancelled -> refunded.\nNo value names refunded, none, cancelled; the column reads back as refunded, none, open, wrong on called off.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 1\n  order  three fields                  column     reads back wrong\n  #1     unpaid, none, cancelled       cancelled  -\n  #2     paid, none, open              paid       -\n  #3     paid, shipped, open           shipped    -\n  #4     refunded, shipped, open       refunded   fulfilment\n  #5     paid, returned, open          shipped    fulfilment\n  #6     refunded, none, cancelled     refunded   called off\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 3\n  question                    three fields          the column\n  money we hold               3: #2 #3 #5           3: #2 #3 #5\n  parcels with the customer   2: #3 #4              2: #3 #5\n  refunds we owe              1: #5                 0: none\nThe column answers 2 of the 3 questions wrong, and raises no error doing it.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n3\n4\n5\n2\n7\nship\n2\n7\npay\n2\n7\nrefund\n2\n3\nreturn\n1\n3\n6\n",
      "screen": "== Order states ==\nPayment, fulfilment and calling off, kept as three fields and as one status column.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 1\n  order  three fields                  column     reads back wrong\n  #1     unpaid, none, open            pending    -\n  #2     paid, none, open              paid       -\n  #3     paid, shipped, open           shipped    -\n  #4     refunded, shipped, open       refunded   fulfilment\n  #5     paid, returned, open          shipped    fulfilment\n  #6     paid, none, cancelled         cancelled  payment\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 3\n  question                    three fields          the column\n  money we hold               4: #2 #3 #5 #6        3: #2 #3 #5\n  parcels with the customer   2: #3 #4              2: #3 #5\n  refunds we owe              2: #5 #6              0: none\nThe column answers 3 of the 3 questions wrong, and raises no error doing it.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 4\nEvery real state, and the value that names it:\n  unpaid, none, open            pending\n  unpaid, shipped, open         -\n  unpaid, returned, open        -\n  paid, none, open              paid\n  paid, shipped, open           shipped\n  paid, returned, open          -\n  refunded, none, open          refunded\n  refunded, shipped, open       -\n  refunded, returned, open      -\n  unpaid, none, cancelled       cancelled\n  unpaid, shipped, cancelled    -\n  unpaid, returned, cancelled   -\n  paid, none, cancelled         -\n  paid, shipped, cancelled      -\n  paid, returned, cancelled     -\n  refunded, none, cancelled     -\n  refunded, shipped, cancelled  -\n  refunded, returned, cancelled -\n5 values name 5 of the 18 real states; 13 have no value, and the code that meets one writes a neighbour.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 5\nOrder #7 placed: unpaid, none, open; the column pending.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 7\nevent (pay, ship, refund, return, cancel)> ship\nOrder #7, ship: three fields unpaid, shipped, open; the column pending -> shipped.\nNo value names unpaid, shipped, open; the column reads back as paid, shipped, open, wrong on payment.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 7\nevent (pay, ship, refund, return, cancel)> pay\nOrder #7, pay: three fields paid, shipped, open; the column shipped -> paid.\nThe column reads back as paid, none, open, wrong on fulfilment, though shipped would name it.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 7\nevent (pay, ship, refund, return, cancel)> refund\nOrder #7, refund: three fields refunded, shipped, open; the column paid -> refunded.\nNo value names refunded, shipped, open; the column reads back as refunded, none, open, wrong on fulfilment.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 2\norder number> 3\nevent (pay, ship, refund, return, cancel)> return\nOrder #3, return: three fields paid, returned, open; the column shipped -> shipped.\nNo value names paid, returned, open; the column reads back as paid, shipped, open, wrong on fulfilment.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 1\n  order  three fields                  column     reads back wrong\n  #1     unpaid, none, open            pending    -\n  #2     paid, none, open              paid       -\n  #3     paid, returned, open          shipped    fulfilment\n  #4     refunded, shipped, open       refunded   fulfilment\n  #5     paid, returned, open          shipped    fulfilment\n  #6     paid, none, cancelled         cancelled  payment\n  #7     refunded, shipped, open       refunded   fulfilment\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 3\n  question                    three fields          the column\n  money we hold               4: #2 #3 #5 #6        3: #2 #3 #5\n  parcels with the customer   2: #4 #7              2: #3 #5\n  refunds we owe              3: #3 #5 #6           0: none\nThe column answers 3 of the 3 questions wrong, and raises no error doing it.\n\n1) orders  2) an event  3) reports  4) every state  5) new order  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "status-field-conflation",
      "caseId": "290-status-field-conflation",
      "title": "Status field conflation — the states that cannot be written down"
    }
  ],
  "updated": "2026-10-18"
}
