Filtering a Party/Role CRUD based on relationship type, using the same controller

Hi there everyone,
I have a tradicional “Party/Role Model” in my app:
- Party ( an organization, a person, etc)
- PartyRelationship ( One or more relationships between parties. They can have relationship types: customer, supplier, reseller, etc)

However, I need to create two “shorcuts” :

  • one to customers CRUD and

  • one to suppliers CRUD , even if they´re actually both “parties” behind the scenes.

I´d like to show on my navigation menu:

  1. A “Customers” link routing to the customer crud. (Actually, the party CRUD, knowing ahead it would be dealing with a “customer” party relationship)

  2. A “Supplier” link routing to the supplier crud. (Actually, the same party CRUD, knowing ahead it would be dealing with a “customer” party relationship)

These links will also need to filter the parties being listed, based on their relationship types.

What´s the recommended approach to this?

Thanks in advance!

Regard.
Leandro

Hi Leandro,

I would go for two controllers here. A CustomersController and a SuppliersController. You would just need to handle the Party model appropriately in each one of those controllers.

Best regards,

// Marco

Sorry, Leandro, but I do not agree with you. However I may not understand what you have offered. Just seems to me that the solution is model-drive. Where in the Party and PartyRelationship controller aspects of CRUD can be managed in building respective association calls. Like << to create, .destroy to delete, etc… Maybe at a maintenance level Role/Customer and CustomerType/Supplier Controllers would be appropriate

(Forgive I am writing quickly)

I would suggest a model structure like the following:

class Party < ActiveRecord::Base
has_many :party_relationships
has_many :roles
end

class Role < ActiveRecord::Base
belongs_to :party_relationship
end

class CustomerType < ActiveRecord::Base
belongs_to :party_relationship
end

class PartyRelationship < ActiveRecord::Base
belongs_to :party

customer, supplier, reseller

scope :customer, -> { where(customer_type_id: 1) } ## id values are specific as to new table/record creation and to my seed below
scope :supplier, -> { where(customer_type_id: 2) }
scope :reseller, -> { where(customer_type_id: 3) }
has_many :customer_types
end

Offering seed.rb

Assuming you are starting anew

unless Party.count > 0
Party.create!(name: ‘Person#1’, role_id: 2 ) ## id: 1
Party.create!(name: ‘Person#2’, role_id: 2 ) ## id: 2
Party.create!(name: ‘Vendor#1’, role_id: 3 ) ## id: 3
Party.create!(name: ‘Vendor#2’, role_id: 3 ) ## id: 4
Party.create!(name: ‘Company#1’, role_id: 1 ) ## id: 5
Party.create!(name: ‘Company#2’, role_id: 1 ) ## id: 6
end

unless PartyRelationship.count > 0
PartyRelationship.create!(party_id: 1, customer_type_id: 1 ) ## Person 1 is an Person that is a customer
PartyRelationship.create!(party_id: 2, customer_type_id: 2 ) ## Person 2 is an Person that is a supplier
PartyRelationship.create!(party_id: 3, customer_type_id: 3 ) ## Vendor 1 is an Companu that is a reseller
PartyRelationship.create!(party_id: 4, customer_type_id: 1 ) ## Vendor 2 is an company that is a customer
PartyRelationship.create!(party_id: 5, customer_type_id: 2 ) ## Company 1 is an organization that is a supplier
PartyRelationship.create!(party_id: 6, customer_type_id: 3 ) ## Company 2 is an organization that is a reselller
end

PartyRelationship.create!(party_id: 1, customer_type_id: 2 ) ## Person 1 is an Person that is a supplier

unless Role.count > 0
Role.create!(role_name: ‘Organization’)
Role.create!(role_name: ‘Person’)
Role.create!(role_name: ‘Company’)
end

unless CustomerType.count > 0
CustomerType.create!(name: ‘customer’)
CustomerType.create!(name: ‘supplier’)
CustomerType.create!(name: ‘reseller’)
end

And migration:

create_table “parties”, force: :cascade do |t|
t.integer “role_id”, limit: 4
t.string “name”, limit: 255
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end

create_table “party_relationships”, force: :cascade do |t|
t.integer “party_id”, limit: 4
t.integer “customer_type_id”, limit: 4
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end

create_table “roles”, force: :cascade do |t|
t.string “role_name”, limit: 255
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end

create_table “customer_types”, force: :cascade do |t|
t.string “name”, limit: 255
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end

Then you should be able to make association calls like

Party.find(1).party_relationships.customer

There may be many errors here, but the spirit is that one can perform CRUD given the Party and PartyRelationship model through their respective controllers… FORGIVE me if I am wrong.

Liz

Sorry… I am addressing Marco… :wink:
Liz

Hey Liz and Marco, thanks a lot for your inputs.

I´ll provide a few more details from what I already have and some needs:

  • A Party is an Organization or a Person. One of the parties is also defined as the “proprietary party”. It´s the organization actually running the app.

  • A Party have many relationships with other parties and there are many relationthip types: supplier, customer, employee, reseller, etc.

  • “John is employee at ACME CORP”

  • “Mary is employee at ACME CORP”

  • “ACME-CA is reseller from ACME CORP”

  • “XYZ is customer from ACME CORP”

  • PWL is customer from ACME CORP"
    What I already have:

  • A STI based model, where a Party have the types: Organization and Person:

Party

  • class Party < ActiveRecord::Base
       has_many :   party_relationships
    has_many :second_parties, :through => :   party_relationships
    scope :organizations, -> { where(type: 'Organization') }
       scope :people, -> { where(type: 'Person') }
    
        def self.      types
    %w(Organization Person)
        end
    end
    

    Organization

    class Organization < Party
    end
    

    Person

    class Person < Party
    end
    
  • A Party Relationship Model:

PartRelationship

  • class PartyRelationship < ActiveRecord::Base
      belongs_to :party, :class_name=>'Party'
      belongs_to :second_party, :class_name => 'Party'
      enum relation: [ :customer, :supplier, :employee, :reseller ]
    end
    
  • I currently have on PartyController who deals with both party subtypes. What I need is just a “shorcut” to the crud model.

  • I ended up using these routes below, pointing to the same party controller and dealing with some conditions based on params[:relation] inside controller to know what to do:

Hi Elizabeth,

I did not want to spend your or anyone else’s time.
I only recognized that more information would help after I saw your first answer.

I´m sorry about that.

Anyway, I´m open to criticism and I´ll try to improve next time.

Regards.

ok… I do not know what a ‘STI based model’ is. And I do not see your SecondParty class here. Pretty sure the matter of second-party can be managed in Party with a field like is_second_party. Don’t understand necessity of Classes Organization and Person

Addressing from what I understand:

class Party < ActiveRecord::Base
has_many :party_relationships

   scope :organizations, -> { where(type: 'Organization') }   ## Haven't tested
scope :people, -> { where(type: 'Person') } ## Haven't tested

one to many party to types

SQL table parties should contain field ‘types’

rails generate migration add_type_to_parties type:string

rake db:migrate

<%= form_for @party … do |f| %>

<%= f.collection_select :type, Party::TYPES, :to_s, :humanize %>

TYPES = %w[Organization Person]

end

class PartyRelationship < ActiveRecord::Base
belongs_to :party

http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html

Declare an enum attribute where the values map to integers in the database, but can be queried by name. So table PartyRelationship must

contain field relation

enum relation: [ :customer, :supplier, :employee, :reseller ]

eg, corresponding database values : 1:customer, 2:supplier, 3:employee, 4:reseller

With regard to CRUD, obviously the enum is hard-coded. Pretty sure that all will need to be done manually and very carefully as to respect values

already stored on the database

With regard to PartyRelationship Controller, record using relation, you will have to be mindful of your enum structure, eg, order

Find

irb(main):003:0> PartyRelationship.find(1).relation

PartyRelationship Load (1.0ms) SELECT party_relationships.* FROM party_relationships WHERE party_relationships.id = 1 LIMIT 1

=> “supplier”

update

irb(main):008:0> PartyRelationship.find(1).update(relation: 3)

PartyRelationship Load (0.0ms) SELECT party_relationships.* FROM party_relationships WHERE party_relationships.id = 1 LIMIT 1

(0.0ms) BEGIN

SQL (13.0ms) UPDATE party_relationships SET relation = 3, updated_at = ‘2015-08-25 14:00:58’ WHERE party_relationships.id = 1

(1.0ms) COMMIT

=> true

end

Hope this helps…

Thanks Elizabeth,

From STI, I mean I´m using Single Table Inheritance , since Person and Organization have many fields in common (name, addresses, etc) , but some specific behaviors to be implemented in the future.
The SecondParty just maps the related party association on PartyRelationship. Its is self-referential association.

I´ll check your suggestions on enum field and relationship controller.
Looks like the´ll help.

Thanks again,
Leandro

Check out ‘super’ on inheritance from Parent Model Class.