Party System Vendor Example
Developer guide for the Enezaframework party system. Part of the Party System series.
Table of Contents
Overview
This is a complete, party-based VendorTable in an accountpayable app. It demonstrates the whole pattern in one place: a master model with PartyModelMixin, a normalized vendortype party category, forward-compatible defaults, and standard CRUD views with no party-specific overrides.
The Model
python
# accountpayable/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",
)
blocked = models.BooleanField(
default=False,
verbose_name="Blocked",
)
sequence_field_name = 'vendorno'
document_name = 'VENDOR'
@property
def party_category(self):
return self.vendortype
def get_party_defaults(self):
defaults = super().get_party_defaults()
defaults['display_name'] = self.searchname or self.vendorno or 'Vendor'
defaults['is_active'] = not self.blocked
return defaults
def __str__(self):
if self.party and self.party.display_name:
return self.party.display_name
return self.searchname or 'Vendor'
class Meta:
db_table = 'vendor'
verbose_name = "Vendor"
verbose_name_plural = "Vendors"
python
# accountpayable/models/__init__.py
from .vendortable import VendorTable
Note how get_party_defaults() mirrors the business blocked flag into the party's is_active, so disabling a vendor also deactivates its party.
The Views
python
# accountpayable/views/vendor_views.py
from enezaframework.views import (
BaseListView, BaseDetailView, BaseCreateView, BaseDeleteView,
)
from enezaframework.utils import auto_url_register
from accountpayable.models import VendorTable
@auto_url_register
class VendorTableListView(BaseListView):
model = VendorTable
def set_list_fields(self):
return ['vendorno', 'party', 'vendortype', 'searchname', 'blocked']
def set_search_fields(self):
return ['vendorno', 'searchname']
@auto_url_register
class VendorTableDetailView(BaseDetailView):
model = VendorTable
def detail_ui_view(self):
return [
{
'name': 'Vendor Information',
'icon': 'Home',
'Fieldcolumns': [
{'fields': ['vendorno', 'vendortype']},
{'fields': ['searchname', 'blocked']},
],
},
]
@auto_url_register
class VendorTableCreateView(BaseCreateView):
model = VendorTable
def detail_ui_view(self):
return [
{
'name': 'Vendor Information',
'icon': 'Home',
'Fieldcolumns': [
{'fields': ['vendorno', 'vendortype']},
{'fields': ['searchname', 'blocked']},
],
},
]
@auto_url_register
class VendorTableDeleteView(BaseDeleteView):
model = VendorTable
python
# accountpayable/views/__init__.py
from .vendor_views import *
The detail view has no get_inline_related_objects() and no get_related_object_id() — party inlines and profile resolution are handled by the framework. Do not add 'party' to Fieldcolumns.
URLs and Menu
python
# accountpayable/urls.py
from enezaframework.utils import get_registered_urls
app_name = "accountpayable"
dynamic_urls = get_registered_urls(app_name)
urlpatterns = [
*dynamic_urls,
]
python
# accountpayable/apps.py
from django.apps import AppConfig
from enezaframework.utils import load_icon
class AccountpayableConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "accountpayable"
menus = [
{
"fasttab": "ap_masterdata",
"name": "AP Master Data",
"icon": load_icon("users_icon"),
"menuitems": [
{
"url": f"{name}:vendortable_list",
"name": "Vendors",
"icon": load_icon("vendor_icon"),
},
],
},
]
The Full Chain
flowchart LR
M["VendorTable<br/>PartyModelMixin"] --> V["VendorTableDetailView<br/>BaseDetailView"]
V --> L["detail_ui_view<br/>Fieldcolumns"]
V --> S["@auto_url_register"]
S --> U["urls.py<br/>get_registered_urls"]
V --> N["apps.py menus"]
U --> PAGE["CRUD pages"]
N --> PAGE
M --> SIG["post_save signal"]
SIG --> PARTY["Party created"]
PARTY --> PROF["Person/Business profile"]
V --> INLINE["Party inlines on detail page"]
PARTY --> INLINEerDiagram
VendorTable }o--|| Party : "party"
Party ||--o| Person : "person_profile"
Party ||--o| Business : "business_profile"
Party ||--o{ Address : "addresses"
Party ||--o{ ContactInformation : "contact_information"At runtime:
- Saving a vendor creates the vendor row, then the framework creates the
Partyfromget_party_defaults(). - The party type (
PRS/ORG) triggersPersonorBusinesscreation. - The vendor detail page injects Address, Contact Information, and the matching profile inline.
- The menu links to
accountpayable:vendortable_list; the URLs are generated from the registered views.