Party Types & Profiles

Developer guide for the Enezaframework party system. Part of the Party System series.

Table of Contents

  1. Overview
  2. PartyCategory
  3. The Party Models
  4. How the Type Drives the Profile
  5. Relationships Reference
  6. Choosing a Party Type
  7. Model Samples
  8. Related Guides

Overview

Every party-based record resolves to exactly one Party. The party_type of that party is PRS (Person) or ORG (Organization), and it decides which profile the framework creates and which conditional inline appears on the detail page. Addresses and contact information are independent of the type and always available.


PartyCategory

PartyCategory is a TextChoices class in enezacommon/models/choices.py:49.

Value Label Profile created Conditional inline
PRS Person Person Personal Details
ORG Organization Business Business Details

Your model's party_category property must return the field that holds one of these values — usually a customer_type or vendortype field declared with choices=PartyCategory.choices.


The Party Models

All party models live in enezacommon/models/Party.py and inherit EnezaSetup (they are entity-scoped setup records).

Model Table Key fields Relation to Party
Party party party_type, display_name, is_active Hub model
Person person first_name, middle_name, last_name, birthday, email OneToOneField(related_name='person_profile')
Business business business_name, business_registration_number OneToOneField(related_name='business_profile')
Address address address_type, street, city, country, primary_address ForeignKey(related_name='addresses')
ContactInformation contactInformation contact, contact_type, extension, primary_contact ForeignKey(related_name='contact_information')

Party itself is the shared record:

python
class Party(EnezaSetup):
    party_type = models.CharField(
        db_column='PartyType',
        max_length=5,
        choices=PartyCategory.choices,
        verbose_name="Party Type",
    )
    display_name = models.CharField(
        db_column='DisplayName',
        max_length=255,
        blank=True,
        null=True,
        verbose_name="Display Name",
    )
    is_active = models.BooleanField(
        db_column='IsActive',
        default=True,
        verbose_name="Is Active",
    )

Do not create Person/Business/Address/ContactInformation manually for party-based entities. The profile is created by the signal chain; addresses and contacts are managed by the party inline views. See Party Inline System.


How the Type Drives the Profile

When a Party is created, create_org_or_person_profile (enezacommon/signals.py:43) branches on party_type.

flowchart TD
    PARTY[Party created] --> TYPE{party_type}
    TYPE -->|PRS| PERSON["Person.objects.get_or_create(party=...)"] 
    TYPE -->|ORG| BUSINESS["Business.objects.get_or_create(party=...)"]
    PERSON --> PVIEW[Personal Details inline]
    BUSINESS --> BVIEW[Business Details inline]
    PARTY --> STATIC[Address + Contact Information inlines]
erDiagram
    PARTY ||--o| PERSON : "person_profile (O2O)"
    PARTY ||--o| BUSINESS : "business_profile (O2O)"
    PARTY ||--o{ ADDRESS : "addresses (FK)"
    PARTY ||--o{ CONTACTINFORMATION : "contact_information (FK)"

    PARTY {
        string party_type "PRS or ORG"
        string display_name
        bool is_active
    }
    PERSON {
        string first_name
        string last_name
        string email
    }
    BUSINESS {
        string business_name
        string business_registration_number
    }
    ADDRESS {
        string street
        string city
        string country
    }
    CONTACTINFORMATION {
        string contact
        string contact_type
    }

Relationships Reference

From To Type Reverse accessor Used for
Your model Party FK PROTECT, nullable %(class)s_party Links the business record to its party.
Party Person OneToOne, nullable person_profile Person profile for PRS.
Party Business OneToOne, nullable business_profile Organization profile for ORG.
Party Address FK, nullable addresses Any number of addresses, both types.
Party ContactInformation FK, nullable contact_information Any number of contacts, both types.
Party PartyContact FK / FK contacts, contact_for Optional party-to-party contact roles.

Choosing a Party Type

  • Choose PRS when the record represents an individual — a private customer, a contact person, an employee.
  • Choose ORG when the record represents a company or legal entity — a corporate customer, a supplier, a branch.

The type is not fixed by the model; it is stored per record through the party category field. A single Customer model can hold both private and corporate customers, and each gets the correct profile and inline.


Model Samples

A model whose type field uses PartyCategory.choices:

python
# myapp/models/vendortable.py
from django.db import models
from enezaframework.models import EnezaMaster, PartyModelMixin
from enezacommon.models.choices import PartyCategory


class VendorTable(PartyModelMixin, EnezaMaster):
    vendorno = models.CharField(
        max_length=50,
        verbose_name="Vendor No.",
    )
    searchname = models.CharField(
        max_length=255,
        verbose_name="Search Name",
    )
    vendortype = models.CharField(
        max_length=5,
        choices=PartyCategory.choices,
        verbose_name="Vendor Type",
    )

    @property
    def party_category(self):
        return self.vendortype

Reading the profile for a saved record:

python
vendor = VendorTable.objects.get(vendorno="VEND0001")
party = vendor.party

if party.party_type == PartyCategory.PERSON:
    profile = party.person_profile
else:
    profile = party.business_profile