Skip to content

Commit

Permalink
Fix push route for multiple routes change
Browse files Browse the repository at this point in the history
  • Loading branch information
hunterboerner committed Feb 25, 2025
1 parent 6f99cae commit e492231
Show file tree
Hide file tree
Showing 12 changed files with 471 additions and 27 deletions.
104 changes: 102 additions & 2 deletions lib/scheduler/scheduler_agent.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
GenServer.start_link(__MODULE__, %{location_id: location})
end

def add_sales_order(pid, sales_order) do
GenServer.call(pid, {:add_sales_order, sales_order})
end

def add_peer(pid, {_location, _product, _pid} = peer) do
GenServer.call(pid, {:add_peer, peer})
end
Expand Down Expand Up @@ -36,6 +40,10 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
GenServer.call(pid, {:propagate_push_route, quantity, product_id})
end

def propagate_pull_route(pid, quantity, product_id) do
GenServer.call(pid, {:propagate_pull_route, quantity, product_id})
end

def add_purchase_order(pid, purchase_order) do
GenServer.call(pid, {:add_purchase_order, purchase_order})
end
Expand All @@ -62,6 +70,7 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
state = Map.put(state, :new_movements, [])
state = Map.put(state, :stale_movements, %{})
state = Map.put(state, :purchase_orders, [])
state = Map.put(state, :sales_orders, [])
{:ok, state}
end

Expand Down Expand Up @@ -97,6 +106,12 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
end)
end

defp get_from_peer_for_product(state, product_id) do
Enum.find(state.peers, fn {{_, product_id_m}, _} ->
product_id_m == product_id
end)
end

def handle_call({:set_product_inventory, {product, amount}}, _from, state) do
{:reply, :ok, put_inv(product, amount, state)}
end
Expand All @@ -113,7 +128,8 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
{:reply, :ok, Map.put(state, :stale_movements, [movement | state.stale_movements])}
end

def handle_call({:propagate_push_route, quantity, product_id}, _from, state) do
def handle_call({:propagate_push_route, quantity, product_id}, _from, state)
when not is_nil(quantity) do
# TODO modify a stale movement if possible.
peer = get_to_peer_for_product(state, product_id)

Expand All @@ -135,6 +151,27 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
end
end

def handle_call({:propagate_pull_route, quantity, product_id}, _from, state) do
peer = get_from_peer_for_product(state, product_id)

if peer do
{{location_id, _product_id}, pid} = peer

movement = %{
quantity: quantity,
product_id: product_id,
to_inventory_id: state.inventory_id,
from_inventory_id: location_id
}

propagate_pull_route(pid, quantity, product_id)

{:reply, :ok, add_movements(state, movement)}
else
{:reply, :ok, state}
end
end

def handle_call({:add_purchase_order, purchase_order}, _from, state) do
accts = generate_po_accts(purchase_order)

Expand Down Expand Up @@ -214,7 +251,7 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
from_location_id: from_inventory_id,
to_location_id: to_inventory_id,
product_id: product_id,
quantity: Money.new(quantity, :XIT)
quantity: Decimal.new(quantity)
})}
end

Expand All @@ -236,6 +273,51 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
{:reply, :ok, state}
end

def handle_call({:add_sales_order, sales_order}, _from, state) do
# Add sales order to the scheduler
accts = generate_so_accts(sales_order)

movements =
for item <- sales_order.sales_lines do
peer = get_from_peer_for_product(state, item.product_id)

if peer do
{{location_id, _product_id}, pid} = peer
propagate_pull_route(pid, item.quantity, item.product_id)

[
%{
quantity: item.quantity,
product_id: item.product_id,
from_inventory_id: state.location_id,
to_inventory_id: accts[item.id].id
},
%{
quantity: item.quantity,
product_id: item.product_id,
to_inventory_id: state.location_id,
from_inventory_id: location_id
}
]
else
[
%{
quantity: item.quantity,
product_id: item.product_id,
to_inventory_id: accts[item.id].id,
from_inventory_id: state.location_id
}
]
end
end
|> List.flatten()

{:reply, :ok,
state
|> Map.put(:sales_orders, [sales_order | state.sales_orders])
|> add_movements(movements)}
end

defp find_route_for_product(product_id, state) do
state.to_peers[product_id]
end
Expand All @@ -258,6 +340,24 @@ defmodule TheronsErp.Scheduler.SchedulerAgent do
end
end

defp get_so_identifier(so, so_item) do
"so.#{so.id}.#{so_item.id}"
end

defp generate_so_accts(so) do
for item <- so.sales_lines, into: %{} do
acct =
TheronsErp.Ledger.Account
|> Ash.Changeset.for_create(:open, %{
currency: "XIT",
identifier: get_so_identifier(so, item)
})
|> Ash.create!()

{item.id, acct}
end
end

defp get_inv_identifier(type, location_id, product) do
TheronsErp.Inventory.Movement.get_inv_identifier(type, location_id, product.identifier)
end
Expand Down
1 change: 1 addition & 0 deletions lib/therons_erp/inventory.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ defmodule TheronsErp.Inventory do
end

resources do
resource TheronsErp.Inventory.ProductRoutes
resource TheronsErp.Inventory.Routes
resource TheronsErp.Inventory.Movement
resource TheronsErp.Inventory.Location
Expand Down
4 changes: 2 additions & 2 deletions lib/therons_erp/inventory/movement.ex
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ defmodule TheronsErp.Inventory.Movement do
%{
from_account_id: from_account_id,
to_account_id: to_account_id,
amount: amount
amount: Money.new(amount, :XIT)
}
)
|> Ash.create()
Expand All @@ -94,7 +94,7 @@ defmodule TheronsErp.Inventory.Movement do
attributes do
uuid_primary_key :id

attribute :quantity, :money
attribute :quantity, :decimal

attribute :manually_created, :boolean, default: false

Expand Down
14 changes: 9 additions & 5 deletions lib/therons_erp/inventory/product.ex
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,7 @@ defmodule TheronsErp.Inventory.Product do
:category_id,
:saleable,
:purchaseable,
:cost,
:route_id
:cost
]
end

Expand All @@ -52,8 +51,7 @@ defmodule TheronsErp.Inventory.Product do
:category_id,
:saleable,
:purchaseable,
:cost,
:route_id
:cost
]
end

Expand Down Expand Up @@ -94,7 +92,13 @@ defmodule TheronsErp.Inventory.Product do
relationships do
belongs_to :category, TheronsErp.Inventory.ProductCategory

belongs_to :route, TheronsErp.Inventory.Routes
many_to_many :routes, TheronsErp.Inventory.Routes do
through TheronsErp.Inventory.ProductRoutes
source_attribute :id
source_attribute_on_join_resource :product_id
destination_attribute :id
destination_attribute_on_join_resource :routes_id
end

has_one :replenishments, TheronsErp.Purchasing.Replenishment
end
Expand Down
31 changes: 31 additions & 0 deletions lib/therons_erp/inventory/product_routes.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
defmodule TheronsErp.Inventory.ProductRoutes do
use Ash.Resource,
otp_app: :therons_erp,
domain: TheronsErp.Inventory,
data_layer: AshPostgres.DataLayer

postgres do
table "product_routes"
repo TheronsErp.Repo
end

actions do
read :read do
primary? true
end

create :create do
accept [:product_id, :routes_id]
primary? true
end

destroy :destroy do
primary? true
end
end

relationships do
belongs_to :product, TheronsErp.Inventory.Product, primary_key?: true, allow_nil?: false
belongs_to :routes, TheronsErp.Inventory.Routes, primary_key?: true, allow_nil?: false
end
end
9 changes: 7 additions & 2 deletions lib/therons_erp/inventory/routes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,13 @@ defmodule TheronsErp.Inventory.Routes do
end

relationships do
has_many :products, TheronsErp.Inventory.Product do
destination_attribute :route_id
many_to_many :products, TheronsErp.Inventory.Product do
through TheronsErp.Inventory.ProductRoutes
source_attribute :id
source_attribute_on_join_resource :routes_id

destination_attribute :id
destination_attribute_on_join_resource :product_id
end
end
end
32 changes: 18 additions & 14 deletions lib/therons_erp/scheduler.ex
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,22 @@ defmodule TheronsErp.Scheduler do
products =
TheronsErp.Inventory.Product
|> Ash.Query.for_read(:list)
|> Ash.read!(load: [:route])
|> Ash.read!(load: [:routes])

for product <- products do
route = product.route

if route do
for r <- route.routes do
to = location_map[r.to_location_id]
from = location_map[r.from_location_id]

if route.type == :push do
SchedulerAgent.add_to_peer(from, {r.to_location_id, product.id, to})
else
SchedulerAgent.add_peer(to, {r.from_location_id, product.id, from})
routes = product.routes

for route <- routes do
if route do
for r <- route.routes do
to = location_map[r.to_location_id]
from = location_map[r.from_location_id]

if route.type == :push do
SchedulerAgent.add_to_peer(from, {r.to_location_id, product.id, to})
else
SchedulerAgent.add_peer(to, {r.from_location_id, product.id, from})
end
end
end
end
Expand All @@ -79,10 +81,12 @@ defmodule TheronsErp.Scheduler do
sales_orders =
TheronsErp.Sales.SalesOrder
|> Ash.Query.filter(state in [:ready, :invoiced])
|> Ash.read!(load: [:sales_lines])
|> Ash.read!(load: [sales_lines: [product: [:routes]]])

for sales_order <- sales_orders do
SchedulerAgent.add_sales_order()
for sales_line <- sales_order.sales_lines do
SchedulerAgent.add_sales_line(sales_line)
end
end

for {_loc, agent} <- location_map do
Expand Down
55 changes: 55 additions & 0 deletions priv/repo/migrations/20250225192853_many_to_many_routes.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
defmodule TheronsErp.Repo.Migrations.ManyToManyRoutes do
@moduledoc """
Updates resources based on their most recent snapshots.
This file was autogenerated with `mix ash_postgres.generate_migrations`
"""

use Ecto.Migration

def up do
alter table(:products) do
remove :route_id
end

create table(:product_routes, primary_key: false) do
add :product_id,
references(:products,
column: :id,
name: "product_routes_product_id_fkey",
type: :uuid,
prefix: "public"
),
primary_key: true,
null: false

add :routes_id,
references(:routes,
column: :id,
name: "product_routes_routes_id_fkey",
type: :uuid,
prefix: "public"
),
primary_key: true,
null: false
end
end

def down do
drop constraint(:product_routes, "product_routes_product_id_fkey")

drop constraint(:product_routes, "product_routes_routes_id_fkey")

drop table(:product_routes)

alter table(:products) do
add :route_id,
references(:routes,
column: :id,
name: "products_route_id_fkey",
type: :uuid,
prefix: "public"
)
end
end
end
Loading

0 comments on commit e492231

Please sign in to comment.