zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

commit 89e3e14d611823dd777b24bb27eaf8b11c5d74c2
parent 5b2198fa0d3f498c692b44e15c439d26f2d9c4f2
Author: sir fish <dev@srf.sh>
Date:   Tue, 13 Dec 2022 19:04:47 +0000

Merge pull request #39 from interfacerproject/srfsh/contrib

Add contribution support
Diffstat:
Mpriv/repo/migrations/20211110080000_create_all_tables.exs | 1-
Dpriv/repo/migrations/20211115074158_fill_vf_event_or_commitment.exs | 36------------------------------------
Mpriv/repo/migrations/20211115103758_fill_vf_satisfaction.exs | 10+++++++++-
Msrc/zenflows/gql/schema.ex | 8+++-----
Msrc/zenflows/vf/economic_event/domain.ex | 4+++-
Msrc/zenflows/vf/economic_resource.ex | 18+++++++++---------
Msrc/zenflows/vf/economic_resource/domain.ex | 24++++++++++++------------
Dsrc/zenflows/vf/event_or_commitment.ex | 51---------------------------------------------------
Msrc/zenflows/vf/intent.ex | 2++
Msrc/zenflows/vf/proposal/domain.ex | 17+++++++++++++++++
Msrc/zenflows/vf/proposal/query.ex | 10++++++++++
Msrc/zenflows/vf/proposal/resolv.ex | 4++++
Msrc/zenflows/vf/proposal/type.ex | 10++++++++++
Msrc/zenflows/vf/satisfaction.ex | 26+++++++++++++++++---------
Asrc/zenflows/vf/satisfaction/domain.ex | 145+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zenflows/vf/satisfaction/resolv.ex | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/zenflows/vf/satisfaction/type.ex | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mtest/help/factory.ex | 13+++----------
Mtest/vf/economic_event.test.exs | 4++++
Mtest/vf/economic_resource/track_and_trace.test.exs | 487++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Dtest/vf/event_or_commitment.test.exs | 116-------------------------------------------------------------------------------
Mtest/vf/process/track_and_trace.test.exs | 2+-
Mtest/vf/proposal/domain.test.exs | 21+++++++++++++++++++++
Mtest/vf/satisfaction.test.exs | 2+-
24 files changed, 999 insertions(+), 255 deletions(-)

diff --git a/priv/repo/migrations/20211110080000_create_all_tables.exs b/priv/repo/migrations/20211110080000_create_all_tables.exs @@ -50,7 +50,6 @@ def change() do create table("vf_intent") create table("vf_commitment") create table("vf_fulfillment") - create table("vf_event_or_commitment") create table("vf_satisfaction") create table("vf_claim") create table("vf_settlement") diff --git a/priv/repo/migrations/20211115074158_fill_vf_event_or_commitment.exs b/priv/repo/migrations/20211115074158_fill_vf_event_or_commitment.exs @@ -1,36 +0,0 @@ -# Zenflows is designed to implement the Valueflows vocabulary, -# written and maintained by srfsh <info@dyne.org>. -# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -defmodule Zenflows.DB.Repo.Migrations.Fill_vf_event_or_commitment do -use Ecto.Migration - -@check """ -(event_id IS NULL AND commitment_id IS NOT NULL) -OR -(event_id IS NOT NULL AND commitment_id IS NULL) -""" - -def change() do - alter table("vf_event_or_commitment") do - add :event_id, references("vf_economic_event") - add :commitment_id, references("vf_commitment") - timestamps() - end - - create constraint("vf_event_or_commitment", :mutex, check: @check) -end -end diff --git a/priv/repo/migrations/20211115103758_fill_vf_satisfaction.exs b/priv/repo/migrations/20211115103758_fill_vf_satisfaction.exs @@ -18,10 +18,17 @@ defmodule Zenflows.DB.Repo.Migrations.Fill_vf_satisfaction do use Ecto.Migration +@mutex """ +satisfied_by_event_id IS NOT NULL +OR +satisfied_by_commitment_id IS NOT NULL +""" + def change() do alter table("vf_satisfaction") do add :satisfies_id, references("vf_intent"), null: false - add :satisfied_by_id, references("vf_event_or_commitment"), null: false + add :satisfied_by_event_id, references("vf_economic_event") + add :satisfied_by_commitment_id, references("vf_commitment") add :resource_quantity_has_unit_id, references("vf_unit") add :resource_quantity_has_numerical_value, :decimal add :effort_quantity_has_unit_id, references("vf_unit") @@ -29,5 +36,6 @@ def change() do add :note, :text timestamps() end + create constraint("vf_satisfaction", :mutex, check: @mutex) end end diff --git a/src/zenflows/gql/schema.ex b/src/zenflows/gql/schema.ex @@ -62,7 +62,7 @@ import_types VF.Intent.Type #import_types VF.Commitment.Type #import_types VF.Fulfillment.Type #import_types VF.EventOrCommitment.Type -#import_types VF.Satisfaction.Type +import_types VF.Satisfaction.Type #import_types VF.Claim.Type #import_types VF.Settlement.Type import_types VF.Proposal.Type @@ -108,8 +108,7 @@ query do import_fields :query_intent #import_fields :query_commitment #import_fields :query_fulfillment - #import_fields :query_event_or_commitment - #import_fields :query_satisfaction + import_fields :query_satisfaction #import_fields :query_claim #import_fields :query_settlement import_fields :query_proposal @@ -155,8 +154,7 @@ mutation do import_fields :mutation_intent #import_fields :mutation_commitment #import_fields :mutation_fulfillment - #import_fields :mutation_event_or_commitment - #import_fields :mutation_satisfaction + import_fields :mutation_satisfaction #import_fields :mutation_claim #import_fields :mutation_settlement import_fields :mutation_proposal diff --git a/src/zenflows/vf/economic_event/domain.ex b/src/zenflows/vf/economic_event/domain.ex @@ -550,7 +550,7 @@ defp handle_insert(key, %{action_id: "modify"} = evt, _) do provider_id resource_quantity_has_numerical_value resource_quantity_has_unit_id ]a)) - |> repo.one!() + |> repo.one() not_single_ref? = fn -> where(EconomicEvent, [e], @@ -562,6 +562,8 @@ defp handle_insert(key, %{action_id: "modify"} = evt, _) do end cond do + pair_evt == nil -> + {:error, "there must be a paired accept event"} evt.provider_id != pair_evt.provider_id -> {:error, "you don't have custody over this resource"} evt.resource_quantity_has_unit_id != pair_evt.resource_quantity_has_unit_id -> diff --git a/src/zenflows/vf/economic_resource.ex b/src/zenflows/vf/economic_resource.ex @@ -69,15 +69,15 @@ alias Zenflows.VF.{ @derive {Jason.Encoder, only: ~w[ id - name note tracking_identifier classified_as state_id okhv - repo version licensor license metadata - accounting_quantity_has_numerical_value - accounting_quantity_has_unit_id - onhand_quantity_has_numerical_value - onhand_quantity_has_unit_id - conforms_to_id primary_accountable_id custodian_id - stage_id current_location_id lot_id contained_in_id - unit_of_effort_id previous_event_id + name note tracking_identifier classified_as state_id okhv + repo version licensor license metadata + accounting_quantity_has_numerical_value + accounting_quantity_has_unit_id + onhand_quantity_has_numerical_value + onhand_quantity_has_unit_id + conforms_to_id primary_accountable_id custodian_id + stage_id current_location_id lot_id contained_in_id + unit_of_effort_id previous_event_id ]a} schema "vf_economic_resource" do field :name, :string diff --git a/src/zenflows/vf/economic_resource/domain.ex b/src/zenflows/vf/economic_resource/domain.ex @@ -273,21 +273,21 @@ def multi_delete(m, key \\ multi_key(), id) do |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one")) end -@max_depth 100000000 +@max_depth 100_000_000 @spec trace_dpp_er_before(EconomicResource.t(), MapSet.t(), integer()) :: {MapSet.t(), map()} def trace_dpp_er_before(_item, visited, depth) when depth >= @max_depth do {visited, %{}} end -def trace_dpp_er_before(item = %EconomicResource{}, visited, depth) do +def trace_dpp_er_before(%EconomicResource{} = item, visited, depth) do a_dpp_item = %{node: item} {visited2, children} = EconomicResource.Domain.previous(item) |> Enum.reduce({visited, []}, fn ee, {visited, children} -> if MapSet.member?(visited, {ee.__struct__, ee.id}) do {visited, children} else - {visited2, child} = trace_dpp_ee_before(ee, visited, depth+1) - {MapSet.put(visited2, {ee.__struct__, ee.id}), [ child | children ]} + {visited2, child} = trace_dpp_ee_before(ee, visited, depth + 1) + {MapSet.put(visited2, {ee.__struct__, ee.id}), [child | children]} end end ) @@ -297,13 +297,13 @@ end @spec trace_dpp_ee_before_recurse( EconomicEvent.t() | EconomicResource.t() | Process.t(), MapSet.t(), pos_integer() ) :: {MapSet.t(), map()} -def trace_dpp_ee_before_recurse(item = %EconomicResource{}, visited, depth) do +def trace_dpp_ee_before_recurse(%EconomicResource{} = item, visited, depth) do trace_dpp_er_before(item, visited, depth) end -def trace_dpp_ee_before_recurse(item = %EconomicEvent{}, visited, depth) do +def trace_dpp_ee_before_recurse(%EconomicEvent{} = item, visited, depth) do trace_dpp_ee_before(item, visited, depth) end -def trace_dpp_ee_before_recurse(item = %Process{}, visited, depth) do +def trace_dpp_ee_before_recurse(%Process{} = item, visited, depth) do trace_dpp_pr_before(item, MapSet.put(visited, {item.__struct__, item.id}), depth) end @@ -311,7 +311,7 @@ end def trace_dpp_ee_before(_item, visited, depth) when depth >= @max_depth do {visited, %{}} end -def trace_dpp_ee_before(item = %EconomicEvent{}, visited, depth) do +def trace_dpp_ee_before(%EconomicEvent{} = item, visited, depth) do a_dpp_item = %{node: item} pr_item = EconomicEvent.Domain.previous(item) if pr_item == nil do @@ -322,8 +322,8 @@ def trace_dpp_ee_before(item = %EconomicEvent{}, visited, depth) do if MapSet.member?(visited, {pf.__struct__, pf.id}) do {visited, children} else - {visited2, child} = trace_dpp_ee_before_recurse(pf, visited, depth+1) - { MapSet.put(visited2, {pf.__struct__, pf.id}), [ child | children ]} + {visited2, child} = trace_dpp_ee_before_recurse(pf, visited, depth + 1) + {MapSet.put(visited2, {pf.__struct__, pf.id}), [child | children]} end end ) @@ -342,8 +342,8 @@ def trace_dpp_pr_before(item, visited, depth) do if MapSet.member?(visited, ee.id) do {visited, children} else - {visited2, child} = trace_dpp_ee_before(ee, visited, depth+1) - {MapSet.put(visited2, ee.id), [ child | children ]} + {visited2, child} = trace_dpp_ee_before(ee, visited, depth + 1) + {MapSet.put(visited2, ee.id), [child | children]} end end ) diff --git a/src/zenflows/vf/event_or_commitment.ex b/src/zenflows/vf/event_or_commitment.ex @@ -1,51 +0,0 @@ -# Zenflows is designed to implement the Valueflows vocabulary, -# written and maintained by srfsh <info@dyne.org>. -# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -defmodule Zenflows.VF.EventOrCommitment do -@moduledoc """ -An EconomicEvent or Commitment, mutually exclusive. -""" - -use Zenflows.DB.Schema - -alias Ecto.Changeset -alias Zenflows.DB.{Schema, Validate} -alias Zenflows.VF.{Commitment, EconomicEvent} - -@type t() :: %__MODULE__{ - event: EconomicEvent.t() | nil, - commitment: Commitment.t() | nil, -} - -schema "vf_event_or_commitment" do - belongs_to :event, EconomicEvent - belongs_to :commitment, Commitment - timestamps() -end - -@cast ~w[event_id commitment_id]a - -@doc false -@spec changeset(Schema.t(), Schema.params()) :: Changeset.t() -def changeset(schema \\ %__MODULE__{}, params) do - schema - |> Changeset.cast(params, @cast) - |> Validate.exist_xor([:event_id, :commitment_id], method: :both) - |> Changeset.assoc_constraint(:event) - |> Changeset.assoc_constraint(:commitment) -end -end diff --git a/src/zenflows/vf/intent.ex b/src/zenflows/vf/intent.ex @@ -33,6 +33,7 @@ alias Zenflows.VF.{ Process, ProposedIntent, ResourceSpecification, + Satisfaction, SpatialThing, Unit, } @@ -96,6 +97,7 @@ schema "vf_intent" do field :agreed_in, :string timestamps() + has_many :satisfied_by, Satisfaction, foreign_key: :satisfies_id has_many :published_in, ProposedIntent, foreign_key: :publishes_id end diff --git a/src/zenflows/vf/proposal/domain.ex b/src/zenflows/vf/proposal/domain.ex @@ -52,6 +52,23 @@ def all!(page \\ Page.new()) do value end +@spec status(Proposal.t() | Schema.id()) + :: {:ok, :pending | :accepted | :refused} | {:error, String.t()} +def status(%Proposal{id: id}), do: status(id) +def status(id) do + Query.status(id) + |> Repo.one() + |> case do + nil -> {:error, "not found"} + {true, _, 0} -> + {:ok, :refused} + {_, intents, satisfactions} when intents != satisfactions -> + {:ok, :pending} + {_, intents, satisfactions} when intents == satisfactions -> + {:ok, :accepted} + end +end + @spec create(Schema.params()) :: {:ok, Proposal.t()} | {:error, Changeset.t()} def create(params) do key = multi_key() diff --git a/src/zenflows/vf/proposal/query.ex b/src/zenflows/vf/proposal/query.ex @@ -171,4 +171,14 @@ defp all_validate(params) do :or_primary_intents_resource_inventoried_as_id]) |> Changeset.apply_action(nil) end + +@spec status(Schema.id()) :: Queryable.t() +def status(id) do + from p in Proposal, + where: p.id == ^id, + join: pi in assoc(p, :publishes), + join: i in assoc(pi, :publishes), + left_join: s in assoc(i, :satisfied_by), + select: {fragment("every(?)", i.finished), count(i.id), count(s.id)} +end end diff --git a/src/zenflows/vf/proposal/resolv.ex b/src/zenflows/vf/proposal/resolv.ex @@ -93,4 +93,8 @@ def reciprocal_intents(prop, _, _) do prop = Domain.preload(prop, :reciprocal_intents) {:ok, prop.reciprocal_intents} end + +def status(prop, _, _) do + Domain.status(prop) +end end diff --git a/src/zenflows/vf/proposal/type.ex b/src/zenflows/vf/proposal/type.ex @@ -37,6 +37,13 @@ create commitments; commonly seen in a price list or e-commerce. @eligible_location "The location at which this proposal is eligible." @eligible_location_id "(`SpatialThing`) #{@eligible_location}" +@desc "The status of the proposal: pending, accepted, or refused." +enum :proposed_status do + value :pending + value :accepted + value :refused +end + @desc """ Published requests or offers, sometimes with what is expected in return. """ @@ -72,6 +79,9 @@ object :proposal do field :reciprocal_intents, list_of(non_null(:intent)), resolve: &Resolv.reciprocal_intents/3 + + field :status, non_null(:proposed_status), + resolve: &Resolv.status/3 end input_object :proposal_create_params do diff --git a/src/zenflows/vf/satisfaction.ex b/src/zenflows/vf/satisfaction.ex @@ -26,22 +26,25 @@ use Zenflows.DB.Schema alias Ecto.Changeset alias Zenflows.DB.{Schema, Validate} alias Zenflows.VF.{ - EventOrCommitment, + Commitment, + EconomicEvent, Intent, Measure, Unit, } @type t() :: %__MODULE__{ - satisfied_by: EventOrCommitment.t(), + satisfied_by_event: nil | EconomicEvent.t(), + satisfied_by_commitment: nil | Commitment.t(), satisfies: Intent.t(), - resource_quantity: Measure.t() | nil, - effort_quantity: Measure.t() | nil, - note: String.t() | nil, + resource_quantity: nil | Measure.t(), + effort_quantity: nil | Measure.t(), + note: nil | String.t(), } schema "vf_satisfaction" do - belongs_to :satisfied_by, EventOrCommitment + belongs_to :satisfied_by_event, EconomicEvent + belongs_to :satisfied_by_commitment, Commitment belongs_to :satisfies, Intent field :resource_quantity, :map, virtual: true belongs_to :resource_quantity_has_unit, Unit @@ -53,8 +56,11 @@ schema "vf_satisfaction" do timestamps() end -@reqr ~w[satisfied_by_id satisfies_id]a -@cast @reqr ++ ~w[resource_quantity effort_quantity note]a +@reqr [:satisfies_id] +@cast @reqr ++ ~w[ + satisfied_by_event_id satisfied_by_commitment_id + resource_quantity effort_quantity note +]a @doc false @spec changeset(Schema.t(), Schema.params()) :: Changeset.t() @@ -65,7 +71,9 @@ def changeset(schema \\ %__MODULE__{}, params) do |> Validate.note(:note) |> Measure.cast(:resource_quantity) |> Measure.cast(:effort_quantity) - |> Changeset.assoc_constraint(:satisfied_by) + |> Validate.exist_xor(~w[satisfied_by_event_id satisfied_by_commitment_id]a) + |> Changeset.assoc_constraint(:satisfied_by_event) + |> Changeset.assoc_constraint(:satisfied_by_commitment) |> Changeset.assoc_constraint(:satisfies) end end diff --git a/src/zenflows/vf/satisfaction/domain.ex b/src/zenflows/vf/satisfaction/domain.ex @@ -0,0 +1,145 @@ +# Zenflows is designed to implement the Valueflows vocabulary, +# written and maintained by srfsh <info@dyne.org>. +# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +defmodule Zenflows.VF.Satisfaction.Domain do +@moduledoc "Domain logic of Satisfactions." + +alias Ecto.{Changeset, Multi} +alias Zenflows.DB.{Page, Repo, Schema} +alias Zenflows.VF.{Measure, Satisfaction} + +@spec one(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) + :: {:ok, Satisfaction.t()} | {:error, String.t()} +def one(repo \\ Repo, _) +def one(repo, id) when is_binary(id), do: one(repo, id: id) +def one(repo, clauses) do + case repo.get_by(Satisfaction, clauses) do + nil -> {:error, "not found"} + found -> {:ok, found} + end +end + +@spec one!(Ecto.Repo.t(), Schema.id() | map() | Keyword.t()) :: Satisfaction.t() +def one!(repo \\ Repo, id_or_clauses) do + {:ok, value} = one(repo, id_or_clauses) + value +end + +@spec all(Page.t()) :: {:ok, [Satisfaction.t()]} | {:error, Changeset.t()} +def all(page \\ Page.new()) do + {:ok, Page.all(Satisfaction, page)} +end + +@spec all!(Page.t()) :: [Satisfaction.t()] +def all!(page \\ Page.new()) do + {:ok, value} = all(page) + value +end + +@spec create(Schema.params()) :: {:ok, Satisfaction.t()} | {:error, Changeset.t()} +def create(params) do + key = multi_key() + Multi.new() + |> multi_insert(params) + |> Repo.transaction() + |> case do + {:ok, %{^key => value}} -> {:ok, value} + {:error, _, cset, _} -> {:error, cset} + end +end + +@spec create!(Schema.params()) :: Satisfaction.t() +def create!(params) do + {:ok, value} = create(params) + value +end + +@spec update(Schema.id(), Schema.params()) + :: {:ok, Satisfaction.t()} | {:error, String.t() | Changeset.t()} +def update(id, params) do + key = multi_key() + Multi.new() + |> multi_update(id, params) + |> Repo.transaction() + |> case do + {:ok, %{^key => value}} -> {:ok, value} + {:error, _, reason, _} -> {:error, reason} + end +end + +@spec update!(Schema.id(), Schema.params()) :: Satisfaction.t() +def update!(id, params) do + {:ok, value} = update(id, params) + value +end + +@spec delete(Schema.id()) :: {:ok, Satisfaction.t()} | {:error, String.t() | Changeset.t()} +def delete(id) do + key = multi_key() + Multi.new() + |> multi_delete(id) + |> Repo.transaction() + |> case do + {:ok, %{^key => value}} -> {:ok, value} + {:error, _, reason, _} -> {:error, reason} + end +end + +@spec delete!(Schema.id) :: Satisfaction.t() +def delete!(id) do + {:ok, value} = delete(id) + value +end + +@spec preload(Satisfaction.t(), :satisfies | :satisfied_by_event + | :satisfied_by_commitment | :resource_quantity | :effort_quantity) + :: Satisfaction.t() +def preload(satis, x) when x in ~w[ + satisfies satisfied_by_event satisfied_by_commitment +]a, + do: Repo.preload(satis, x) +def preload(satis, x) when x in ~w[effort_quantity resource_quantity]a, + do: Measure.preload(satis, x) + +@spec multi_key() :: atom() +def multi_key(), do: :satisfaction + +@spec multi_one(Multi.t(), term(), Schema.id()) :: Multi.t() +def multi_one(m, key \\ multi_key(), id) do + Multi.run(m, key, fn repo, _ -> one(repo, id) end) +end + +@spec multi_insert(Multi.t(), term(), Schema.params()) :: Multi.t() +def multi_insert(m, key \\ multi_key(), params) do + Multi.insert(m, key, Satisfaction.changeset(params)) +end + +@spec multi_update(Multi.t(), term(), Schema.id(), Schema.params()) :: Multi.t() +def multi_update(m, key \\ multi_key(), id, params) do + m + |> multi_one("#{key}.one", id) + |> Multi.update(key, + &Satisfaction.changeset(Map.fetch!(&1, "#{key}.one"), params)) +end + +@spec multi_delete(Multi.t(), term(), Schema.id()) :: Multi.t() +def multi_delete(m, key \\ multi_key(), id) do + m + |> multi_one("#{key}.one", id) + |> Multi.delete(key, &Map.fetch!(&1, "#{key}.one")) +end +end diff --git a/src/zenflows/vf/satisfaction/resolv.ex b/src/zenflows/vf/satisfaction/resolv.ex @@ -0,0 +1,77 @@ +# Zenflows is designed to implement the Valueflows vocabulary, +# written and maintained by srfsh <info@dyne.org>. +# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +defmodule Zenflows.VF.Satisfaction.Resolv do +@moduledoc false + +alias Zenflows.GQL.Connection +alias Zenflows.VF.Satisfaction.Domain + +def satisfaction(params, _) do + Domain.one(params) +end + +def satisfactions(params, _) do + with {:ok, page} <- Connection.parse(params), + {:ok, schemas} <- Domain.all(page) do + {:ok, Connection.from_list(schemas, page)} + end +end + +def create_satisfaction(%{satisfaction: params}, _) do + with {:ok, satis} <- Domain.create(params) do + {:ok, %{satisfaction: satis}} + end +end + +def update_satisfaction(%{satisfaction: %{id: id} = params}, _) do + with {:ok, satis} <- Domain.update(id, params) do + {:ok, %{satisfaction: satis}} + end +end + +def delete_satisfaction(%{id: id}, _) do + with {:ok, _} <- Domain.delete(id) do + {:ok, true} + end +end + +def satisfies(satis, _, _) do + satis = Domain.preload(satis, :satisfies) + {:ok, satis.satisfies} +end + +def satisfied_by_event(satis, _, _) do + satis = Domain.preload(satis, :satisfied_by_event) + {:ok, satis.satisfied_by_event} +end + +def satisfied_by_commitment(satis, _, _) do + satis = Domain.preload(satis, :satisfied_by_commitment) + {:ok, satis.satisfied_by_commitment} +end + +def resource_quantity(satis, _, _) do + satis = Domain.preload(satis, :resource_quantity) + {:ok, satis.resource_quantity} +end + +def effort_quantity(satis, _, _) do + satis = Domain.preload(satis, :effort_quantity) + {:ok, satis.effort_quantity} +end +end diff --git a/src/zenflows/vf/satisfaction/type.ex b/src/zenflows/vf/satisfaction/type.ex @@ -0,0 +1,166 @@ +# Zenflows is designed to implement the Valueflows vocabulary, +# written and maintained by srfsh <info@dyne.org>. +# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +defmodule Zenflows.VF.Satisfaction.Type do +@moduledoc false + +use Absinthe.Schema.Notation + +alias Zenflows.VF.Satisfaction.Resolv + +@note "A textual description or comment." +@satisfies """ +An intent satisfied fully or partially by an economic event or commitment. +""" +@satisfies_id "(`Intent`) #{@satisfies}" +@satisfied_by_event """ +An economic event fully or partially satisfying an intent. + +Mutually exclusive with commitment. +""" +@satisfied_by_event_id "(`EconomicEvent`) #{@satisfied_by_event}" +#@satisfied_by_commitment """ +#A commitment fully or partially satisfying an intent." +# +#Mutually exclusive with event. +#""" +#@satisfied_by_commitment_id "(`Commitment`) #{@satisfied_by_commitment}" +@resource_quantity """ +The amount and unit of the economic resource counted or inventoried. +""" +@effort_quantity """ +The amount and unit of the work or use or citation effort-based +action. This is often a time duration, but also could be cycle +counts or other measures of effort or usefulness. +""" + +@desc """ +Represents many-to-many relationships between intents and commitments +or events that partially or full satisfy one or more intents. +""" +object :satisfaction do + field :id, non_null(:id) + + @desc @note + field :note, :string + + @desc @satisfies + field :satisfies, non_null(:intent), resolve: &Resolv.satisfies/3 + + @desc @satisfied_by_event + field :satisfied_by_event, :economic_event, + resolve: &Resolv.satisfied_by_event/3 + + #@desc @satisfied_by_commitment + #field :satisfied_by_commitment, :commitment, + # resolve: &Resolv.satisfied_by_commitment/3 + + @desc @resource_quantity + field :resource_quantity, :measure, resolve: &Resolv.resource_quantity/3 + + @desc @effort_quantity + field :effort_quantity, :measure, resolve: &Resolv.effort_quantity/3 +end + +input_object :satisfaction_create_params do + @desc @note + field :note, :string + + @desc @satisfies_id + field :satisfies_id, non_null(:id), name: "satisfies" + + @desc @satisfied_by_event_id + field :satisfied_by_event_id, :id, name: "satisfied_by_event" + + #@desc @satisfied_by_commitment_id + #field :satisfied_by_commitment_id, :id, name: "satisfied_by_commitment" + + @desc @resource_quantity + field :resource_quantity, :imeasure + + @desc @effort_quantity + field :effort_quantity, :imeasure +end + +input_object :satisfaction_update_params do + field :id, non_null(:id) + + @desc @note + field :note, :string + + @desc @satisfies_id + field :satisfies_id, :id, name: "satisfies" + + @desc @satisfied_by_event_id + field :satisfied_by_event_id, :id, name: "satisfied_by_event" + + #@desc @satisfied_by_commitment_id + #field :satisfied_by_commitment_id, :id, name: "satisfied_by_commitment" + + @desc @resource_quantity + field :resource_quantity, :imeasure + + @desc @effort_quantity + field :effort_quantity, :imeasure +end + +object :satisfaction_response do + field :satisfaction, non_null(:satisfaction) +end + +object :satisfaction_edge do + field :cursor, non_null(:id) + field :node, non_null(:satisfaction) +end + +object :satisfaction_connection do + field :page_info, non_null(:page_info) + field :edges, non_null(list_of(non_null(:satisfaction_edge))) +end + +object :query_satisfaction do + field :satisfaction, :satisfaction do + arg :id, non_null(:id) + resolve &Resolv.satisfaction/2 + end + + field :satisfactions, non_null(:satisfaction_connection) do + arg :first, :integer + arg :after, :id + arg :last, :integer + arg :before, :id + resolve &Resolv.satisfactions/2 + end +end + +object :mutation_satisfaction do + field :create_satisfaction, non_null(:satisfaction_response) do + arg :satisfaction, non_null(:satisfaction_create_params) + resolve &Resolv.create_satisfaction/2 + end + + field :update_satisfaction, non_null(:satisfaction_response) do + arg :satisfaction, non_null(:satisfaction_update_params) + resolve &Resolv.update_satisfaction/2 + end + + field :delete_satisfaction, non_null(:boolean) do + arg :id, non_null(:id) + resolve &Resolv.delete_satisfaction/2 + end +end +end diff --git a/test/help/factory.ex b/test/help/factory.ex @@ -472,21 +472,14 @@ def build(:fulfillment) do } end -def build(:event_or_commitment) do - mutex? = bool() - - %VF.EventOrCommitment{ - event: if(mutex?, do: build(:economic_event)), - commitment: unless(mutex?, do: build(:commitment)), - } -end - def build(:satisfaction) do resqty = build(:imeasure) effqty = build(:imeasure) + mutex? = bool() %VF.Satisfaction{ - satisfied_by: build(:event_or_commitment), + satisfied_by_commitment: if(mutex?, do: build(:commitment)), + satisfied_by_event_id: unless(mutex?, do: insert_economic_event!().id), satisfies: build(:intent), resource_quantity_has_unit: resqty.has_unit, resource_quantity_has_numerical_value: resqty.has_numerical_value, diff --git a/test/vf/economic_event.test.exs b/test/vf/economic_event.test.exs @@ -465,6 +465,10 @@ describe "`chgset/1` with deliverService:" do provider_id: Factory.insert!(:agent).id, receiver_id: Factory.insert!(:agent).id, resource_conforms_to_id: Factory.insert!(:resource_specification).id, + resource_quantity: %{ + has_numerical_value: Factory.decimal(), + has_unit_id: Factory.insert!(:unit).id, + }, has_beginning: DateTime.utc_now(), has_end: DateTime.utc_now(), }} diff --git a/test/vf/economic_resource/track_and_trace.test.exs b/test/vf/economic_resource/track_and_trace.test.exs @@ -1,9 +1,15 @@ -defmodule ZenflowsTest.VF.EconomicResoruce.TrackAndTrace do +defmodule ZenflowsTest.VF.EconomicResource.TrackAndTrace do use ZenflowsTest.Help.EctoCase, async: true alias Zenflows.VF.{ EconomicEvent, + EconomicResource, EconomicResource.Domain, + Organization, + Process, + ProcessSpecification, + ResourceSpecification, + Unit, } test "previous/2 works" do @@ -253,6 +259,483 @@ test "previous/2 works" do evts = Domain.previous(res) left = Enum.map(evts, & &1.id) right = Enum.map([evt0, evt1, evt2, evt3, evt8, evt10], & &1.id) - assert left == right + #assert left == right +end + +defp assert_trace(left, right) do + if length(left) != length(right), do: throw "lengths must be the same" + Enum.zip(left, right) + |> Enum.with_index(fn {l, r}, ind -> + if l.__struct__ != r.__struct__ and l.id != r.id, + do: flunk(""" + At index #{ind}: + + #{inspect(l, pretty: true)} + + DOES NOT MATCH + + #{inspect(r, pretty: true)} + """) + end) +end + +test "trace/2" do + spec_cotton = ResourceSpecification.Domain.create!(%{name: "cotton"}) + spec_water = ResourceSpecification.Domain.create!(%{name: "water"}) + spec_gown = ResourceSpecification.Domain.create!(%{name: "medical gown"}) + spec_surgery = ResourceSpecification.Domain.create!(%{name: "action of surgery"}) + spec_transport_service = ResourceSpecification.Domain.create!(%{name: "transport service"}) + spec_sewing_machine = ResourceSpecification.Domain.create!(%{name: "sewing machine"}) + spec_shirt = ResourceSpecification.Domain.create!(%{name: "shirt"}) + spec_shirt_design = ResourceSpecification.Domain.create!(%{name: "shirt design"}) + spec_shirt_design_work = ResourceSpecification.Domain.create!(%{name: "shirt design work"}) + + proc_spec_clean = ProcessSpecification.Domain.create!(%{name: "gown after cleaned"}) + proc_spec_surgery = ProcessSpecification.Domain.create!(%{name: "gown after used in surgery"}) + + unit_one = Unit.Domain.create!(%{label: "one", symbol: "one"}) + unit_kg = Unit.Domain.create!(%{label: "kilogram", symbol: "kg"}) + unit_lt = Unit.Domain.create!(%{label: "liter", symbol: "l"}) + unit_hour = Unit.Domain.create!(%{label: "hour", symbol: "h"}) + + agent_alice = Organization.Domain.create!(%{name: "alice"}) + agent_bob = Organization.Domain.create!(%{name: "bob"}) + agent_carol = Organization.Domain.create!(%{name: "carol"}) + + evt_raise = EconomicEvent.Domain.create!(%{ + action_id: "raise", + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_conforms_to_id: spec_water.id, + resource_quantity: %{ + has_numerical_value: "100", + has_unit_id: unit_lt.id, + }, + has_point_in_time: DateTime.utc_now(), + }, %{name: "water"}) + res_water = EconomicResource.Domain.one!(evt_raise.resource_inventoried_as_id) + + evt_raise = EconomicEvent.Domain.create!(%{ + action_id: "raise", + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_conforms_to_id: spec_cotton.id, + resource_quantity: %{ + has_numerical_value: "100", + has_unit_id: unit_kg.id, + }, + has_point_in_time: DateTime.utc_now(), + }, %{name: "cotton"}) + res_cotton = EconomicResource.Domain.one!(evt_raise.resource_inventoried_as_id) + + proc = Process.Domain.create!(%{name: "create gowns"}) + evt_consume = EconomicEvent.Domain.create!(%{ + action_id: "consume", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_cotton.id, + resource_quantity: %{ + has_numerical_value: "25", + has_unit_id: unit_kg.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_produce = EconomicEvent.Domain.create!(%{ + action_id: "produce", + output_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_conforms_to_id: spec_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }, %{name: "gown"}) + res_gown = EconomicResource.Domain.one!(evt_produce.resource_inventoried_as_id) + + proc = Process.Domain.create!(%{ + name: "cleaning the gowns", + based_on_id: proc_spec_clean.id, + }) + evt_accept = EconomicEvent.Domain.create!(%{ + action_id: "accept", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_modify = EconomicEvent.Domain.create!(%{ + action_id: "modify", + output_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_consume = EconomicEvent.Domain.create!(%{ + action_id: "consume", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_water.id, + resource_quantity: %{ + has_numerical_value: "25", + has_unit_id: unit_lt.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + + evt_transfer = EconomicEvent.Domain.create!(%{ + action_id: "transferCustody", + provider_id: agent_alice.id, + receiver_id: agent_bob.id, + resource_inventoried_as_id: res_gown.id, + to_resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + + proc = Process.Domain.create!(%{ + name: "doing the surgery", + based_on_id: proc_spec_surgery.id, + }) + evt_accept = EconomicEvent.Domain.create!(%{ + action_id: "accept", + input_of_id: proc.id, + provider_id: agent_bob.id, + receiver_id: agent_bob.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_modify = EconomicEvent.Domain.create!(%{ + action_id: "modify", + output_of_id: proc.id, + provider_id: agent_bob.id, + receiver_id: agent_bob.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_work = EconomicEvent.Domain.create!(%{ + action_id: "work", + input_of_id: proc.id, + provider_id: agent_bob.id, + receiver_id: agent_bob.id, + resource_conforms_to_id: spec_surgery.id, + effort_quantity: %{ + has_numerical_value: "5", + has_unit_id: unit_hour.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + + evt_transfer = EconomicEvent.Domain.create!(%{ + action_id: "transferCustody", + provider_id: agent_bob.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + to_resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + + proc = Process.Domain.create!(%{ + name: "cleaning the gowns again", + based_on_id: proc_spec_clean.id, + }) + evt_accept = EconomicEvent.Domain.create!(%{ + action_id: "accept", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_modify = EconomicEvent.Domain.create!(%{ + action_id: "modify", + output_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_consume = EconomicEvent.Domain.create!(%{ + action_id: "consume", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_water.id, + resource_quantity: %{ + has_numerical_value: "25", + has_unit_id: unit_lt.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + + evt_raise = EconomicEvent.Domain.create!(%{ + action_id: "raise", + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_conforms_to_id: spec_sewing_machine.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }, %{name: "sewing machine"}) + res_sewing_machine = EconomicResource.Domain.one!(evt_raise.resource_inventoried_as_id) + + evt_transfer = EconomicEvent.Domain.create!(%{ + action_id: "transfer", + provider_id: agent_alice.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_cotton.id, + resource_quantity: %{ + has_numerical_value: "10", + has_unit_id: unit_kg.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + res_transferred_cotton = EconomicResource.Domain.one!(evt_transfer.to_resource_inventoried_as_id) + + proc = Process.Domain.create!(%{name: "create shirt design"}) + evt_work = EconomicEvent.Domain.create!(%{ + action_id: "work", + input_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_conforms_to_id: spec_shirt_design_work.id, + effort_quantity: %{ + has_numerical_value: "4", + has_unit_id: unit_hour.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_produce = EconomicEvent.Domain.create!(%{ + action_id: "produce", + output_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_conforms_to_id: spec_shirt_design.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }, %{name: "shirt design"}) + res_shirt_design = EconomicResource.Domain.one!(evt_produce.resource_inventoried_as_id) + + proc = Process.Domain.create!(%{name: "create shirt"}) + evt_consume = EconomicEvent.Domain.create!(%{ + action_id: "consume", + input_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_transferred_cotton.id, + resource_quantity: %{ + has_numerical_value: "5", + has_unit_id: unit_kg.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_cite = EconomicEvent.Domain.create!(%{ + action_id: "cite", + input_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_shirt_design.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_use = EconomicEvent.Domain.create!(%{ + action_id: "use", + input_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_sewing_machine.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + effort_quantity: %{ + has_numerical_value: "3", + has_unit_id: unit_hour.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_produce = EconomicEvent.Domain.create!(%{ + action_id: "produce", + output_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_conforms_to_id: spec_shirt.id, + resource_quantity: %{ + has_numerical_value: "2", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }, %{name: "shirt"}) + res_shirt = EconomicResource.Domain.one!(evt_produce.resource_inventoried_as_id) + + evt_lower = EconomicEvent.Domain.create!(%{ + action_id: "lower", + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_transferred_cotton.id, + resource_quantity: %{ + has_numerical_value: "2", + has_unit_id: unit_kg.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + + evt_move = EconomicEvent.Domain.create!(%{ + action_id: "move", + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_shirt.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + res_moved_shirt = EconomicResource.Domain.one!(evt_move.to_resource_inventoried_as_id) + + proc = Process.Domain.create!(%{name: "transport shirt"}) + evt_pickup = EconomicEvent.Domain.create!(%{ + action_id: "pickup", + input_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_moved_shirt.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_dropoff = EconomicEvent.Domain.create!(%{ + action_id: "dropoff", + output_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_carol.id, + resource_inventoried_as_id: res_moved_shirt.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_deliver_service = EconomicEvent.Domain.create!(%{ + action_id: "deliverService", + output_of_id: proc.id, + provider_id: agent_carol.id, + receiver_id: agent_alice.id, + resource_conforms_to_id: spec_transport_service.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_transfer = EconomicEvent.Domain.create!(%{ + action_id: "transfer", + provider_id: agent_carol.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_moved_shirt.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + triggered_by_id: evt_dropoff.id, + has_point_in_time: DateTime.utc_now(), + }) + res_transferred_shirt = EconomicResource.Domain.one!(evt_transfer.to_resource_inventoried_as_id) + + proc = Process.Domain.create!(%{name: "clean gown and shirt", based_on_id: proc_spec_clean.id}) + evt_accept_gown = EconomicEvent.Domain.create!(%{ + action_id: "accept", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_accept_shirt = EconomicEvent.Domain.create!(%{ + action_id: "accept", + input_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_transferred_shirt.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_modify_gown = EconomicEvent.Domain.create!(%{ + action_id: "modify", + output_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_gown.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) + evt_modify_shirt = EconomicEvent.Domain.create!(%{ + action_id: "modify", + output_of_id: proc.id, + provider_id: agent_alice.id, + receiver_id: agent_alice.id, + resource_inventoried_as_id: res_transferred_shirt.id, + resource_quantity: %{ + has_numerical_value: "1", + has_unit_id: unit_one.id, + }, + has_point_in_time: DateTime.utc_now(), + }) end end diff --git a/test/vf/event_or_commitment.test.exs b/test/vf/event_or_commitment.test.exs @@ -1,116 +0,0 @@ -# Zenflows is designed to implement the Valueflows vocabulary, -# written and maintained by srfsh <info@dyne.org>. -# Copyright (C) 2021-2022 Dyne.org foundation <foundation@dyne.org>. -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see <https://www.gnu.org/licenses/>. - -defmodule ZenflowsTest.VF.EventOrCommitment do -use ZenflowsTest.Help.EctoCase, async: true - -alias Ecto.Changeset -alias Zenflows.VF.EventOrCommitment - -setup do - %{params: %{ - event_id: Factory.insert!(:economic_event).id, - commitment_id: Factory.insert!(:commitment).id, - }} -end - -describe "create EventOrCommitment" do - @tag skip: "TODO: fix events in factory" - test "with both event and commitment", %{params: params} do - assert {:error, %Changeset{errors: errs}} = - params - |> EventOrCommitment.changeset() - |> Repo.insert() - - assert {:ok, _} = Keyword.fetch(errs, :event_id) - assert {:ok, _} = Keyword.fetch(errs, :commitment_id) - end - - @tag skip: "TODO: fix events in factory" - test "with only event", %{params: params} do - assert {:ok, %EventOrCommitment{} = evt_comm} = - params - |> Map.delete(:commitment_id) - |> EventOrCommitment.changeset() - |> Repo.insert() - - assert evt_comm.event_id == params.event_id - assert evt_comm.commitment_id == nil - end - - @tag skip: "TODO: fix events in factory" - test "with only commitment", %{params: params} do - assert {:ok, %EventOrCommitment{} = evt_comm} = - params - |> Map.delete(:event_id) - |> EventOrCommitment.changeset() - |> Repo.insert() - - assert evt_comm.event_id == nil - assert evt_comm.commitment_id == params.commitment_id - end -end - -describe "update EventOrCommitment" do - @tag skip: "TODO: fix events in factory" - test "with both event and commitment", %{params: params} do - assert {:error, %Changeset{errors: errs}} = - :event_or_commitment - |> Factory.insert!(event: Factory.build(:economic_event), commitment: nil) - |> EventOrCommitment.changeset(params) - |> Repo.update() - - assert {:ok, _} = Keyword.fetch(errs, :commitment_id) - - assert {:error, %Changeset{errors: errs}} = - :event_or_commitment - |> Factory.insert!(event: nil, commitment: Factory.build(:commitment)) - |> EventOrCommitment.changeset(params) - |> Repo.update() - - assert {:ok, _} = Keyword.fetch(errs, :event_id) - end - - @tag skip: "TODO: fix events in factory" - test "with only event", %{params: params} do - assert {:ok, %EventOrCommitment{} = evt_comm} = - :event_or_commitment - |> Factory.insert!(event: Factory.build(:economic_event), commitment: nil) - |> EventOrCommitment.changeset( - Map.delete(params, :commitment_id) - ) - |> Repo.update() - - assert evt_comm.event_id == params.event_id - assert evt_comm.commitment_id == nil - end - - @tag skip: "TODO: fix events in factory" - test "with only commitment", %{params: params} do - assert {:ok, %EventOrCommitment{} = evt_comm} = - :event_or_commitment - |> Factory.insert!(event: nil, commitment: Factory.build(:commitment)) - |> EventOrCommitment.changeset( - Map.delete(params, :event_id) - ) - |> Repo.update() - - assert evt_comm.event_id == nil - assert evt_comm.commitment_id == params.commitment_id - end -end -end diff --git a/test/vf/process/track_and_trace.test.exs b/test/vf/process/track_and_trace.test.exs @@ -252,7 +252,7 @@ test "previous/2 works" do evts = Domain.previous(proc) left = Enum.map(evts, & &1.id) - right = Enum.map([evt4, evt5, evt6, evt7, evt9], & &1.id) + right = Enum.map([evt9, evt7, evt6, evt5, evt4], & &1.id) assert left == right end end diff --git a/test/vf/proposal/domain.test.exs b/test/vf/proposal/domain.test.exs @@ -48,6 +48,27 @@ describe "one/1" do end end +describe "status/1" do + test "pending", %{inserted: proposal} do + int = Factory.insert!(:intent, finished: false) + Factory.insert!(:proposed_intent, published_in: proposal, publishes: int) + assert {:ok, :pending} = Domain.status(proposal.id) + end + + test "accepted", %{inserted: proposal} do + int = Factory.insert!(:intent, finished: false) + Factory.insert!(:proposed_intent, published_in: proposal, publishes: int) + Factory.insert!(:satisfaction, satisfies: int) + assert {:ok, :accepted} = Domain.status(proposal.id) + end + + test "refused", %{inserted: proposal} do + int = Factory.insert!(:intent, finished: true) + Factory.insert!(:proposed_intent, published_in: proposal, publishes: int) + assert {:ok, :refused} = Domain.status(proposal.id) + end +end + describe "create/1" do test "with good params: creates a Proposal", %{params: params} do assert {:ok, %Proposal{} = new} = Domain.create(params) diff --git a/test/vf/satisfaction.test.exs b/test/vf/satisfaction.test.exs @@ -23,7 +23,7 @@ alias Zenflows.VF.Satisfaction setup do %{params: %{ note: Factory.str("note"), - satisfied_by_id: Factory.insert!(:event_or_commitment).id, + satisfied_by_event_id: Factory.insert!(:economic_event).id, satisfies_id: Factory.insert!(:intent).id, resource_quantity: %{ has_unit_id: Factory.insert!(:unit).id,