economic_event.ex (11529B)
1 # Zenflows is designed to implement the Valueflows vocabulary, 2 # written and maintained by srfsh <info@dyne.org>. 3 # Copyright (C) 2021-2023 Dyne.org foundation <foundation@dyne.org>. 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU Affero General Public License as published by 7 # the Free Software Foundation, either version 3 of the License, or 8 # (at your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU Affero General Public License for more details. 14 # 15 # You should have received a copy of the GNU Affero General Public License 16 # along with this program. If not, see <https://www.gnu.org/licenses/>. 17 18 defmodule Zenflows.VF.EconomicEvent do 19 @moduledoc """ 20 An observed economic flow, as opposed to a flow planned to happen in 21 the future. This could reflect a change in the quantity of an economic 22 resource. It is also defined by its behavior in relation to the economic 23 resource. 24 """ 25 use Zenflows.DB.Schema 26 27 alias Ecto.Changeset 28 alias Zenflows.DB.{Schema, Validate} 29 alias Zenflows.VF.{ 30 Action, 31 Agent, 32 Agreement, 33 EconomicEvent, 34 EconomicResource, 35 Measure, 36 Process, 37 ResourceSpecification, 38 SpatialThing, 39 Unit, 40 } 41 42 @type t() :: %__MODULE__{ 43 action: Action.t(), 44 input_of: Process.t() | nil, 45 output_of: Process.t() | nil, 46 provider: Agent.t(), 47 receiver: Agent.t(), 48 resource_inventoried_as: EconomicResource.t() | nil, 49 to_resource_inventoried_as: EconomicResource.t() | nil, 50 resource_classified_as: [String.t()] | nil, 51 resource_conforms_to: ResourceSpecification.t() | nil, 52 resource_quantity: Measure.t() | nil, 53 effort_quantity: Measure.t() | nil, 54 has_beginning: DateTime.t() | nil, 55 has_end: DateTime.t() | nil, 56 has_point_in_time: DateTime.t() | nil, 57 note: String.t() | nil, 58 to_location: SpatialThing.t() | nil, 59 at_location: SpatialThing.t() | nil, 60 realization_of: Agreement.t() | nil, 61 # in_scope_of: 62 agreed_in: String.t() | nil, 63 triggered_by: EconomicEvent.t() | nil, 64 previous_event: nil | EconomicEvent.t(), 65 } 66 67 @derive {Jason.Encoder, only: ~w[ 68 id 69 action_id 70 resource_classified_as 71 resource_quantity_has_numerical_value 72 effort_quantity_has_numerical_value 73 has_beginning has_end has_point_in_time 74 note agreed_in 75 input_of_id output_of_id 76 provider_id receiver_id 77 resource_inventoried_as_id to_resource_inventoried_as_id 78 resource_conforms_to_id 79 resource_quantity_has_unit_id effort_quantity_has_unit_id 80 to_location_id at_location_id 81 realization_of_id 82 triggered_by_id 83 previous_event_id 84 ]a} 85 schema "vf_economic_event" do 86 field :action_id, Action.ID 87 field :action, :map, virtual: true 88 belongs_to :input_of, Process 89 belongs_to :output_of, Process 90 belongs_to :provider, Agent 91 belongs_to :receiver, Agent 92 belongs_to :resource_inventoried_as, EconomicResource 93 belongs_to :to_resource_inventoried_as, EconomicResource 94 field :resource_classified_as, {:array, :string} 95 belongs_to :resource_conforms_to, ResourceSpecification 96 field :resource_quantity, :map, virtual: true 97 belongs_to :resource_quantity_has_unit, Unit 98 field :resource_quantity_has_numerical_value, :decimal 99 field :effort_quantity, :map, virtual: true 100 belongs_to :effort_quantity_has_unit, Unit 101 field :effort_quantity_has_numerical_value, :decimal 102 field :has_beginning, :utc_datetime_usec 103 field :has_end, :utc_datetime_usec 104 field :has_point_in_time, :utc_datetime_usec 105 field :note, :string 106 belongs_to :to_location, SpatialThing 107 belongs_to :at_location, SpatialThing 108 belongs_to :realization_of, Agreement 109 # field :in_scope_of 110 field :agreed_in, :string 111 belongs_to :triggered_by, EconomicEvent 112 belongs_to :previous_event, EconomicEvent 113 timestamps() 114 end 115 116 @insert_reqr ~w[action_id provider_id receiver_id]a 117 @insert_cast @insert_reqr ++ ~w[ 118 has_beginning has_end has_point_in_time note at_location_id 119 realization_of_id agreed_in triggered_by_id previous_event_id 120 ]a 121 122 # insert changeset 123 @doc false 124 @spec changeset(Schema.params()) :: Changeset.t() 125 def changeset(params) do 126 %__MODULE__{} 127 |> Changeset.cast(params, @insert_cast) 128 |> Changeset.validate_required(@insert_reqr) 129 |> Validate.exist_or([:has_point_in_time, :has_beginning, :has_end]) 130 |> Validate.exist_nand([:has_point_in_time, :has_beginning]) 131 |> Validate.exist_nand([:has_point_in_time, :has_end]) 132 |> do_changeset() 133 |> Validate.uri(:agreed_in) 134 |> Validate.note(:note) 135 |> Validate.class(:resource_classified_as) 136 |> Changeset.assoc_constraint(:input_of) 137 |> Changeset.assoc_constraint(:output_of) 138 |> Changeset.assoc_constraint(:provider) 139 |> Changeset.assoc_constraint(:receiver) 140 |> Changeset.assoc_constraint(:resource_inventoried_as) 141 |> Changeset.assoc_constraint(:to_resource_inventoried_as) 142 |> Changeset.assoc_constraint(:resource_conforms_to) 143 |> Changeset.assoc_constraint(:to_location) 144 |> Changeset.assoc_constraint(:at_location) 145 |> Changeset.assoc_constraint(:realization_of) 146 |> Changeset.assoc_constraint(:triggered_by) 147 |> Changeset.assoc_constraint(:previous_event) 148 end 149 150 @spec do_changeset(Changeset.t()) :: Changeset.t() 151 defp do_changeset(%{valid?: false} = cset), do: cset 152 defp do_changeset(%{changes: %{action_id: "raise"}} = cset) do 153 cset 154 |> Changeset.cast(cset.params, ~w[ 155 resource_conforms_to_id resource_inventoried_as_id 156 resource_classified_as resource_quantity to_location_id 157 ]a) 158 |> Changeset.validate_required([:resource_quantity]) 159 |> Measure.cast(:resource_quantity) 160 |> Validate.value_eq([:provider_id, :receiver_id]) 161 |> Validate.exist_xor([:resource_conforms_to_id, :resource_inventoried_as_id]) 162 end 163 defp do_changeset(%{changes: %{action_id: "produce"}} = cset) do 164 cset 165 |> Changeset.cast(cset.params, ~w[ 166 output_of_id resource_conforms_to_id resource_inventoried_as_id 167 resource_classified_as resource_quantity to_location_id 168 ]a) 169 |> Changeset.validate_required(~w[output_of_id resource_quantity]a) 170 |> Measure.cast(:resource_quantity) 171 |> Validate.value_eq([:provider_id, :receiver_id]) 172 |> Validate.exist_xor([:resource_conforms_to_id, :resource_inventoried_as_id]) 173 end 174 defp do_changeset(%{changes: %{action_id: "lower"}} = cset) do 175 cset 176 |> Changeset.cast(cset.params, ~w[resource_inventoried_as_id resource_quantity]a) 177 |> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a) 178 |> Validate.value_eq([:provider_id, :receiver_id]) 179 |> Measure.cast(:resource_quantity) 180 end 181 defp do_changeset(%{changes: %{action_id: "consume"}} = cset) do 182 cset 183 |> Changeset.cast(cset.params, ~w[input_of_id resource_inventoried_as_id resource_quantity]a) 184 |> Changeset.validate_required(~w[input_of_id resource_inventoried_as_id resource_quantity]a) 185 |> Measure.cast(:resource_quantity) 186 |> Validate.value_eq([:provider_id, :receiver_id]) 187 end 188 defp do_changeset(%{changes: %{action_id: "use"}} = cset) do 189 cset 190 |> Changeset.cast(cset.params, ~w[ 191 input_of_id effort_quantity 192 resource_inventoried_as_id resource_conforms_to_id 193 resource_quantity 194 ]a) 195 |> Changeset.validate_required(~w[input_of_id effort_quantity]a) 196 |> Measure.cast(:effort_quantity) 197 |> Measure.cast(:resource_quantity) 198 |> Validate.exist_xor([:resource_inventoried_as_id, :resource_conforms_to_id]) 199 end 200 defp do_changeset(%{changes: %{action_id: "work"}} = cset) do 201 cset 202 |> Changeset.cast(cset.params, ~w[input_of_id effort_quantity resource_conforms_to_id]a) 203 |> Changeset.validate_required(~w[input_of_id effort_quantity resource_conforms_to_id]a) 204 |> Measure.cast(:effort_quantity) 205 end 206 defp do_changeset(%{changes: %{action_id: "cite"}} = cset) do 207 cset 208 |> Changeset.cast(cset.params, ~w[ 209 input_of_id resource_quantity 210 resource_inventoried_as_id resource_conforms_to_id 211 ]a) 212 |> Changeset.validate_required(~w[input_of_id resource_quantity]a) 213 |> Measure.cast(:resource_quantity) 214 |> Validate.exist_xor([:resource_inventoried_as_id, :resource_conforms_to_id]) 215 end 216 defp do_changeset(%{changes: %{action_id: "deliverService"}} = cset) do 217 cset 218 |> Changeset.cast(cset.params, ~w[input_of_id output_of_id resource_conforms_to_id resource_quantity]a) 219 |> Changeset.validate_required(~w[resource_conforms_to_id resource_quantity]a) 220 |> Measure.cast(:resource_quantity) 221 |> Validate.value_ne([:input_of_id, :output_of_id]) 222 end 223 defp do_changeset(%{changes: %{action_id: "pickup"}} = cset) do 224 cset 225 |> Changeset.cast(cset.params, ~w[input_of_id resource_quantity resource_inventoried_as_id]a) 226 |> Changeset.validate_required(~w[input_of_id resource_quantity resource_inventoried_as_id]a) 227 |> Measure.cast(:resource_quantity) 228 |> Validate.value_eq([:provider_id, :receiver_id]) 229 end 230 defp do_changeset(%{changes: %{action_id: "dropoff"}} = cset) do 231 cset 232 |> Changeset.cast(cset.params, ~w[output_of_id resource_quantity resource_inventoried_as_id to_location_id]a) 233 |> Changeset.validate_required(~w[output_of_id resource_quantity resource_inventoried_as_id]a) 234 |> Measure.cast(:resource_quantity) 235 |> Validate.value_eq([:provider_id, :receiver_id]) 236 end 237 defp do_changeset(%{changes: %{action_id: "accept"}} = cset) do 238 cset 239 |> Changeset.cast(cset.params, ~w[input_of_id resource_quantity resource_inventoried_as_id]a) 240 |> Changeset.validate_required(~w[input_of_id resource_quantity resource_inventoried_as_id]a) 241 |> Measure.cast(:resource_quantity) 242 |> Validate.value_eq([:provider_id, :receiver_id]) 243 end 244 defp do_changeset(%{changes: %{action_id: "modify"}} = cset) do 245 cset 246 |> Changeset.cast(cset.params, ~w[output_of_id resource_quantity resource_inventoried_as_id]a) 247 |> Changeset.validate_required(~w[output_of_id resource_quantity resource_inventoried_as_id]a) 248 |> Measure.cast(:resource_quantity) 249 |> Validate.value_eq([:provider_id, :receiver_id]) 250 end 251 defp do_changeset(%{changes: %{action_id: "combine"}} = cset) do 252 cset 253 |> Changeset.cast(cset.params, ~w[]a) 254 |> Changeset.validate_required(~w[]a) 255 end 256 defp do_changeset(%{changes: %{action_id: "separate"}} = cset) do 257 cset 258 |> Changeset.cast(cset.params, ~w[]a) 259 |> Changeset.validate_required(~w[]a) 260 end 261 defp do_changeset(%{changes: %{action_id: "transferAllRights"}} = cset) do 262 cset 263 |> Changeset.cast(cset.params, ~w[ 264 resource_inventoried_as_id to_resource_inventoried_as_id 265 resource_quantity resource_classified_as 266 ]a) 267 |> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a) 268 |> Measure.cast(:resource_quantity) 269 end 270 defp do_changeset(%{changes: %{action_id: id}} = cset) 271 when id in ~w[transferCustody transfer] do 272 cset 273 |> Changeset.cast(cset.params, ~w[ 274 resource_inventoried_as_id to_resource_inventoried_as_id resource_quantity 275 to_location_id resource_classified_as 276 ]a) 277 |> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a) 278 |> Measure.cast(:resource_quantity) 279 end 280 defp do_changeset(%{changes: %{action_id: "move"}} = cset) do 281 cset 282 |> Changeset.cast(cset.params, ~w[ 283 resource_inventoried_as_id to_resource_inventoried_as_id resource_quantity 284 to_location_id resource_classified_as 285 ]a) 286 |> Changeset.validate_required(~w[resource_inventoried_as_id resource_quantity]a) 287 |> Measure.cast(:resource_quantity) 288 |> Validate.value_eq([:provider_id, :receiver_id]) 289 end 290 291 @update_cast ~w[note agreed_in realization_of_id triggered_by_id]a 292 293 # update changeset 294 @doc false 295 @spec changeset(Schema.t(), Schema.params()) :: Changeset.t() 296 def changeset(schema, params) do 297 schema 298 |> Changeset.cast(params, @update_cast) 299 |> Validate.note(:note) 300 |> Changeset.assoc_constraint(:realization_of) 301 |> Changeset.assoc_constraint(:triggered_by) 302 end 303 end