Just want to share the bug I caused and spent too long debugging.
I got this order item that has amount
(required amount) and amount_shipped
on it. You know, sometimes shipping isn’t done in a single batch.
Instead of writing a plain and simple context function, I thought it was a good idea to use a changeset with an update_change
to streamline the process and the client only has to supply a new value to amount_shipped
, thus avoiding a controller action, a context function and potentially a virtual field.
So I ended up with something like this:
def changeset(order_item, %{amount_shipped: _} = attrs) do
order_item
|> cast(attrs, [:amount_shipped])
|> update_change(:amount_shipped, &(&1 + order_item.amount_shipped))
end
The atom key amount_shipped
in pattern matching is just because this particular changeset clause is only used with processed data. Irrelevant here.
This approach worked relatively well, until some point where a weird bug was reported. Sometimes, the amount_shipped
is not updated.
This took me way too long than it should have taken to figure out… The bug turned out to be simple, if 500 was already shipped and you ship another 500, this is not a “change” and does not trigger update_change
.
I could have taken the chance to rewrite it back to plain and simple albeit more verbose solutions… but I ended up with this fix:
def changeset(order_item, %{amount_shipped: amount_shipped} = attrs) do
order_item
|> cast(attrs, [:amount_shipped])
# in case same amount
|> force_change(:amount_shipped, amount_shipped)
|> update_change(:amount_shipped, &(&1 + order_item.amount_shipped))
end
Not promoting the trick or anything. Just sharing some head-scratching fun I had.