module Ecoportal module API class Internal # @attr account [Account, nil] the account of the person or `nil` if missing. class Person < V1::Person class_resolver :person_details_class, "Ecoportal::API::Internal::PersonDetails" class_resolver :person_account_class, "Ecoportal::API::Internal::Account" embeds_one :account, nullable: true, klass: :person_account_class def initialize(doc = {}, *args, **kargs, &block) super(doc, *args, **kargs, &block) @is_new = @prev_is_new = no_account_nor_details?(@doc) end def consolidate! @prev_is_new = @is_new @is_new = false super end def reset!(*args) @is_new = @prev_is_new super(*args) end # @note # - `original_doc` should technically hold the model as it is on **server side** # - Once this person has been successfullyupdated, `#consolidate!` should be called. # - The above sets a copy of `doc` as `original_doc` (meaning: _no pending changes_) # - Therefore we can safely assume that this is a **new record** if the `original_doc` # does not have details nor account (as all people should have either to exist). # - **However**, to the purpose of getting a clean `as_update`, `original_doc` is filled # with `details` right on creation. For this reason, a workaround is necessary via `@is_new` # @return [Boolean] whether or not this entry is being created now (i.e. non existent on server) def new?(value = :original) return @is_new if value == :original no_account_nor_details?(initial_doc) end # @return [Boolean] if the account has been added to `doc` def account_added?(value = :original) ref_doc = (value == :original) ? original_doc : initial_doc !!account && !ref_doc["account"] end # @return [Boolean] if the account has been removed from `doc` def account_removed?(value = :original) ref_doc = (value == :original) ? original_doc : initial_doc !account && !!ref_doc["account"] end def as_json super.update("account" => account&.as_json) end def as_update(ref = :last, ignore: []) super(ref, ignore: ignore | ["user_id", "permissions_merged", "prefilter"]) end # Sets the Account to the person, depending on the paramter received: # - `nil`: blanks the account. # - `Account`: sets a copy of the object param as account. # - `Hash`: slices the properties of `Account` (keeping the value of `user_id` if there was already account). # @note this method does not make dirty the account (meaning that `as_json` will be an empty hash `{}`) # @param value [nil, Account, Hash] value to be set. # @return [nil, Account] the resulting `Account` set to the person. def account=(value) case value when NilClass doc["account"] = nil when Internal::Account doc["account"] = JSON.parse(value.to_json) when Hash user_id = account.user_id if account doc["account"] = value.slice(*Internal::Account::PROPERTIES) doc["account"]["user_id"] = user_id if user_id else # TODO raise "Invalid set on account: Need nil, Account or Hash; got #{value.class}" end remove_instance_variable("@account") if defined?(@account) return account end # Adds an empty account to the person. # @note if the person exists, and does not have an account, an this will send an invite. # @note this will **not** change the account properties of this person. def add_account self.account = {} end private def no_account_nor_details?(value = @doc) return true unless value.is_a?(Hash) !value.key?("details") && !value.key?("account") end end end end end