@asaidimu/erp-types v3.0.1
@asaidimu/erp-types
Table of Contents
- Overview & Features
- Installation & Setup
- Usage Documentation
- Project Architecture
- Development & Contributing
- Additional Information
Overview & Features
What is @asaidimu/erp-types
?
@asaidimu/erp-types
is a comprehensive, modular standard library of TypeScript data model interfaces for building robust and scalable Enterprise Resource Planning (ERP) systems. It provides flexible, type-safe definitions for core business domains, enabling developers to create consistent and interoperable applications across various industries, from manufacturing and retail to service industries and specialized sectors.
This library defines the structure of ERP data without imposing implementation details, allowing for diverse architectural choices (monolithic, microservices) and deployment options (web, mobile, desktop). Its design prioritizes flexibility, strong typing, and audit readiness, making it an ideal foundation for modern business management solutions.
Beyond just TypeScript types, @asaidimu/erp-types
also provides corresponding anansi
schema definitions. These schemas offer a powerful layer for runtime data validation, documentation generation, and dynamic UI forms, ensuring that your application's data conforms to its defined structure not just at compile-time, but also at runtime.
Why Use These Types?
ERP systems are inherently complex, managing deeply interconnected business processes such as inventory tracking, financial auditing, human resources, and project execution. Without a unified and well-defined type system, development can quickly lead to:
- Inconsistency: Ad-hoc data structures result in mismatched APIs and brittle integrations between modules.
- Errors: Lack of type safety increases the likelihood of runtime bugs, such as misinterpreting stock units, mishandling transaction states, or incorrectly assigning roles.
- Redundancy: Developers often find themselves reinventing similar data structures across different parts of the system, leading to wasted effort and increased maintenance burden.
@asaidimu/erp-types
addresses these challenges by:
- Standardizing: Providing a reusable, canonical foundation for common ERP domains, significantly reducing duplication and promoting consistency.
- Ensuring Safety: Leveraging TypeScript's powerful type system to enforce strict type checks at compile-time, catching potential errors before they reach production. The accompanying
anansi
schemas provide a mechanism for robust runtime validation. - Enabling Flexibility: Utilizing extensible generics and abstract interfaces to support both traditional business use cases (e.g., warehouse inventory) and highly unconventional or specialized scenarios (e.g., modeling "nuts" as a currency in a custom economy).
In essence, this library acts as a robust blueprint, ensuring that your ERP's data model is consistent, scalable, maintainable, and adaptable to evolving business needs.
Key Features
- Comprehensive Coverage: Includes TypeScript interfaces and
anansi
schema definitions for 12 core ERP domains, from Logistics and Finance to HR and Project Management. - Dual-Layer Type Safety: Provides strong compile-time type checking with TypeScript and enables robust runtime data validation through
anansi
schema definitions. - Modular Design: Each core module is independent, allowing for progressive adoption and deployment based on specific business requirements.
- Extensibility: Designed with open patterns and generics, making it easy to customize and extend types without modifying the core library.
- Cross-Module Integration: Provides consistent interfaces that facilitate seamless data flow and integration between different business functions.
- Audit Readiness: Supports comprehensive versioning and history tracking capabilities across various modules, crucial for compliance and accountability.
- JSON Compatibility: Types and schemas are aligned with JSON formats for straightforward data exchange, serialization, and storage in modern data systems.
- Unconventional Use Case Support: Generics and flexible data structures enable modeling diverse scenarios beyond traditional enterprise environments (e.g., a "squirrel's nut-based economy").
Core Modules
The library is structured into the following key domains, each offering a rich set of interfaces and schemas:
Logistics Management: Manages physical flow of goods, inventory, storage, and transportation.
- Key Components: Product Management, Inventory Control, Storage Facility Management, Transport Management, Route Planning, Event Tracking.
- Use Cases: Retail inventory, manufacturing supply chain, warehouse operations, fleet management.
Financial Management: Handles monetary aspects with flexible currency support.
- Key Components: Transaction Processing, Account Management, Asset Tracking, Liability Management, Multi-currency Support, Financial History.
- Use Cases: General ledger, accounts receivable/payable, asset management, financial reporting, budget tracking.
Human Resources Management: Manages employee data, compensation, and workforce planning.
- Key Components: Employee Records, Payroll Processing, Benefits Administration, Time & Attendance, Performance Management, Recruitment, Training & Development.
- Use Cases: Employee lifecycle, compensation, workforce analytics, compliance reporting.
Customer Relationship Management (CRM): Manages customer interactions, sales processes, and service delivery.
- Key Components: Contact Management, Sales Pipeline, Interaction History, Service Management, Marketing Integration, Contract Management.
- Use Cases: Sales force automation, customer service, marketing campaign tracking, customer retention.
Project Management: Handles planning, execution, and monitoring of business initiatives.
- Key Components: Project Planning, Resource Allocation, Task Management, Time Tracking, Budget Management, Milestone Tracking.
- Use Cases: Product development, client engagements, internal initiatives, software development.
Manufacturing & Production: Manages creation of goods from raw materials to finished products.
- Key Components: Bill of Materials, Production Planning, Work Orders, Machine Management, Quality Control, Production Reporting.
- Use Cases: Discrete manufacturing, process manufacturing, job shop operations.
Procurement Management: Manages purchasing processes and supplier relationships.
- Key Components: Requisition Management, Supplier Management, Purchase Order Processing, Receiving, Invoice Matching, Contract Management.
- Use Cases: Strategic sourcing, supplier relationship management, purchase approval workflows.
Document Management: Handles creation, storage, and retrieval of business documents.
- Key Components: Document Repository, Version Control, Access Control, Workflow Processing, Template Management, Retention Policies.
- Use Cases: Contract management, policy documentation, technical documentation, legal compliance.
Reporting & Analytics: Provides business intelligence and data visualization capabilities.
- Key Components: Dashboard Creation, Standard Reports, Custom Report Builder, Data Export, Alert System, Predictive Analytics.
- Use Cases: Executive dashboards, operational reporting, financial analysis, sales performance tracking.
Integration Framework: Enables communication between modules and external systems.
- Key Components: API Management, Data Synchronization, Event Processing, External Connectors, Data Transformation, Message Queuing.
- Use Cases: E-commerce platform integration, payment gateway connections, IoT device data collection.
User & Access Management: Controls system security and access rights.
- Key Components: User Authentication, Role-Based Access Control, Multi-factor Authentication, Single Sign-On, Session Management, Audit Logging.
- Use Cases: Security compliance, departmental access restrictions, sensitive information protection.
Compliance & Governance: Ensures adherence to regulations and internal policies.
- Key Components: Regulatory Tracking, Policy Management, Audit Management, Risk Assessment, Compliance Reporting, Issue Management.
- Use Cases: Industry-specific regulation compliance, financial reporting requirements, data protection standards.
System Design Principles
The library is built upon several foundational principles to ensure its robustness and adaptability:
- Flexibility First: Utilizes generic types and extensive metadata support to adapt to diverse business contexts without requiring source code changes.
- Type Safety: Employs strong typing with TypeScript to ensure reliable data handling and minimize runtime errors, providing compile-time guarantees. The inclusion of
anansi
schemas further enforces this at runtime. - Modularity: Designed with independent modules that can be adopted and deployed separately or as a unified system, promoting a microservices-friendly approach.
- Extensibility: Follows open design patterns to easily accommodate future growth, custom data requirements, and domain-specific extensions.
- Cross-Module Integration: Provides consistent interfaces that enable seamless data flow and interaction between different business functions.
- Audit Readiness: Incorporates comprehensive versioning and history tracking capabilities across all modules, essential for compliance, debugging, and historical analysis.
- JSON Compatibility: Ensures direct alignment with JSON schemas, facilitating easy data exchange, serialization, and storage in modern data systems.
Installation & Setup
Prerequisites
To use @asaidimu/erp-types
in your project, you'll need:
- Node.js: Version 14 or higher (recommended: LTS version).
- TypeScript: Version
5.0.3
or higher. - Bun: (Optional, but recommended for development scripts) Version
1.0.0
or higher.
Installation Steps
Install the package using your preferred package manager:
# Using npm
npm install @asaidimu/erp-types
# Using yarn
yarn add @asaidimu/erp-types
# Using bun
bun add @asaidimu/erp-types
Verification
To verify that the types are correctly installed and accessible in your project, create a simple TypeScript file (e.g., test-types.ts
):
import { Person, ISOStringDate } from '@asaidimu/erp-types/types';
interface MyEmployeeData {
name: string;
employeeId: string;
}
interface MyEmployeeMetadata {
hireDate: ISOStringDate;
department: string;
}
// Define a concrete type using the generic `Person` interface
type MyEmployee = Person<MyEmployeeData, 'hr-system', MyEmployeeMetadata>;
// Instantiate an object conforming to the defined type
const employee: MyEmployee = {
id: "EMP789",
data: {
type: "natural", // 'natural' or 'artificial' as per Person type
name: "Jane Doe",
employeeId: "A1B2C3D4",
},
metadata: {
'data-role': 'hr-system', // Matches the DataRole generic argument
hireDate: "2024-01-15T09:00:00Z",
department: "Sales",
},
};
console.log(`Employee Name: ${employee.data.name}`);
console.log(`Employee ID: ${employee.data.employeeId}`);
console.log(`Hire Date: ${employee.metadata.hireDate}`);
// This line would cause a TypeScript compilation error, demonstrating type safety:
// employee.data.nonExistentField = "value";
Compile and run the file:
# Compile
npx tsc test-types.ts
# Run (requires Node.js or Bun)
node test-types.js
# Or with Bun:
bun run test-types.ts
If the compilation succeeds without errors and the script runs, the types are correctly integrated into your project.
Usage Documentation
These types are designed to model ERP data declaratively, providing a strong structural foundation for your application. They do not include any runtime logic or validation, which should be handled by your application layer, optionally using the provided anansi
schemas for robust runtime checks.
Basic Usage Example
Here's how you might import and use a few basic types, specializing them for your domain:
import {
ISOStringDate,
Address,
Geolocation,
Currency,
Transaction,
Account,
Item,
Stock,
Facility,
DATA_ROLE_SALES, // Specific data role for sales transactions
} from '@asaidimu/erp-types/types';
// 1. Define custom data shapes for generic type parameters
// These interfaces hold your domain-specific properties.
interface MyProductData {
material: string;
size: "S" | "M" | "L" | "XL";
}
interface MyProductMetadata {
supplierSKU: string;
}
interface MyProductGroupMetadata {
collection: string;
}
// 2. Define concrete types by applying your custom shapes to the generic ERP interfaces
type ApparelProduct = Item<MyProductData, MyProductMetadata, MyProductGroupMetadata>;
type WarehouseMetadata = { managerId: string };
type WarehouseFacility = Facility<"warehouse", "active" | "inactive", { areaCode: string }, WarehouseMetadata>;
type WarehouseStock = Stock<
ApparelProduct,
"available" | "reserved" | "defective", // Custom stock states
"warehouse", // Custom facility category
"active" | "inactive", // Custom facility states
WarehouseMetadata, // Facility-specific metadata
{ lastInspected: ISOStringDate } // Stock-specific metadata
>;
// For a sales transaction, specify the medium, metadata shape, and data role.
type CashTransaction = Transaction<"cash" | "card", { customerId: string }, unknown, typeof DATA_ROLE_SALES>;
// 3. Create instances of your specialized types
const usd: Currency = {
code: "USD",
symbol: "$",
precision: 2, // 'decimalPlaces' changed to 'precision' based on finance.ts
};
const mainWarehouse: WarehouseFacility = {
id: "WH-NYC-001",
label: "NYC Main Distribution Center",
category: "warehouse",
status: "active",
area: { unit: "sqm", value: 50000 },
data: { floorAreaSqM: 50000 }, // 'floorAreaSqM' added to FacilityData
metadata: { managerId: "EMP-007" },
location: {
latitude: 40.7128,
longitude: -74.0060,
source: "geocoded" // 'source' is an enum in common.ts
}
};
const blueTShirt: ApparelProduct = {
id: "TSHIRT-BLUE-M",
label: "Blue T-Shirt",
description: "Cotton crew neck, medium size, blue color.",
data: {
material: "cotton",
size: "M",
},
metadata: {
supplierSKU: "SUP-A-TSHIRTM-BLUE",
},
group: { // 'group' can be an object or ID. Here, demonstrating as an object.
id: "GRP-TSHIRTS",
label: "T-Shirts",
description: "All t-shirts",
metadata: {
collection: "Summer 2025",
},
parent: undefined // Optional parent group
},
type: "fungible", // 'fungible' or 'non-fungible'
tags: ["apparel", "casual"],
};
const tShirtStock: WarehouseStock = {
id: "STK-TSHIRT-001",
product: blueTShirt, // Can reference the full product object
quantity: 250,
reserved: 10, // Optional reserved quantity
acquired: "2025-03-01T10:00:00Z",
storage: mainWarehouse, // Can reference the full facility object
state: "available",
batch: "BATCH-2025-Q1-TSHIRTS",
expires: "2027-03-01T00:00:00Z", // Example: if apparel had a shelf life
unit: "pieces",
metadata: {
lastInspected: "2025-03-10T14:30:00Z",
},
};
const customerPayment: CashTransaction = {
id: "TXN-SALES-9876",
description: "Customer purchase for 3 T-shirts",
amount: 45.00,
type: "credit", // 'credit' or 'debit'
medium: "card", // Specific medium 'card'
currency: usd,
state: "completed", // Transaction state
metadata: { // Custom metadata for sales transactions
'data-role': DATA_ROLE_SALES, // Required data-role property as per schema
customerId: "CUST-XYZ-123",
transactionChannel: "POS",
},
group: { id: "GRP-SALES", label: "Retail Sales", description: "All retail sales" }, // Optional transaction group
recurrence: undefined, // Not a recurring transaction
gateway: { // Payment gateway details
id: "GATEWAY-STRIPE-TXN-ABC",
status: "success",
direction: "in", // 'in' or 'out'
response: { approvalCode: "ABC123XYZ" } // Example gateway response metadata
},
};
console.log(`Product: ${tShirtStock.product.label}`);
console.log(`Stock Quantity: ${tShirtStock.quantity} ${tShirtStock.unit}`);
console.log(`Transaction Amount: ${customerPayment.currency.symbol}${customerPayment.amount}`);
Real-World Use Case: Managing a Small Retail Chain
Let's illustrate how various modules seamlessly integrate using a scenario where you're building an ERP for a retail chain with two stores, tracking inventory, sales, employee shifts, and a restocking project.
import {
ISOStringDate,
Timespan,
Address,
Geolocation,
Currency,
Person,
WorkLog,
CareerShift,
OrganisationalUnit,
Item,
Facility,
Stock,
StockMovement,
Sale,
Project,
Task,
DATA_ROLE_REAL,
DATA_ROLE_SALES,
} from '@asaidimu/erp-types/types';
// 1. Define Domain-Specific Concrete Types for the Retail Chain
// These types specialize the generic ERP interfaces for our retail business context.
// Logistics Module Types
type RetailProductData = { unit: string; packaging: string };
type RetailProductMetadata = { brand: string; seasonality: string };
type RetailProductGroupMetadata = { category: string; department: string };
type RetailProduct = Item<RetailProductData, RetailProductMetadata, RetailProductGroupMetadata>;
type StoreCategory = "retail-store" | "warehouse-hub" | "returns-center";
type StoreStatus = "open" | "closed" | "under-renovation";
type StoreFacilityData = { floorAreaSqM: number };
type StoreFacilityMetadata = { managerName: string; contactPhone: string };
type RetailFacility = Facility<StoreCategory, StoreStatus, StoreFacilityData, StoreFacilityMetadata>;
type StockState = "available" | "reserved" | "sold" | "defective" | "in-transit" | "on-hold";
type ProductStock = Stock<
RetailProduct,
StockState,
StoreCategory,
StoreStatus,
StoreFacilityMetadata,
{ lastAuditDate: ISOStringDate }
>;
type StockMovementType = "sale" | "transfer-out" | "transfer-in" | "return" | "waste" | "restock";
type ProductStockMovement = StockMovement<
StockState,
StockMovementType,
{ orderId?: string; customerId?: string },
StoreCategory,
StoreStatus,
StoreFacilityMetadata
>;
// Finance Module Types (simplified for this example)
type RetailMedium = "cash" | "card" | "mobile-payment" | "store-credit";
type RetailAccountType = "sales-revenue" | "operating-expenses" | "payroll" | "inventory-asset";
type RetailAccountMetadata = { branchId: string; openedBy: string };
type RetailAccount = Account<RetailAccountType, RetailMedium, RetailAccountMetadata>;
// Human Resources Module Types
type EmployeeRole = "cashier" | "manager" | "stocker" | "security";
interface EmployeePersonalInfo extends Record<string, unknown> { // Extend RealPersonData structure for specific HR data
bio: { name: { first: string; last: string; other?: string[] }; dateOfBirth: ISOStringDate; gender: string; placeOfBirth: Address<any, any>; appearance: any[] };
relationships: any[]; groups: any[]; affiliations: any[]; nationality: any[]; address: any; contact: any[]; history: any; personality: any;
employeeId: string;
currentRole: EmployeeRole;
}
type EmployeeMetadata = { hireDate: ISOStringDate; departmentId: string; status: "active" | "on-leave" | "terminated" };
type RetailEmployee = Person<EmployeePersonalInfo, typeof DATA_ROLE_REAL, EmployeeMetadata>;
type ShiftLogData = { hoursWorked: number; shiftType: "morning" | "afternoon" | "night" };
type ShiftLogMetadata = { approvedByManagerId?: string; clockInLocation?: Geolocation };
type EmployeeShiftLog = WorkLog<ShiftLogData, ShiftLogMetadata>;
type OrgUnitData = { headId: string; budgetCode: string };
type OrgUnitMembershipData = { role: string; startDate: ISOStringDate };
type RetailOrgUnit = OrganisationalUnit<OrgUnitData, OrgUnitMembershipData, { locationId: string }>;
// Sales Module Types
// Re-using Party from sales.ts, which is a specialized Person.
type RetailPartyData =
| ({ type: "natural"; identity: { name: { first: string; last: string; other?: string[] }; }; nationality: { country: string; id?: { number: string; document?: string; }; }; occupation?: string; role?: string; organization?: string; }
| { type: "artificial"; name: string; industry: "retail" | "distribution"; incorporation: { type: "LLC" | "Inc"; number: string; date: string; country: string; document?: string; }; })
& { contact: { email: string; phone?: string; }; address: Address<any, any>; tax: { number: string; document?: string; }; accounts: (string | Account)[]; };
type RetailParty = Person<RetailPartyData, typeof DATA_ROLE_SALES>;
type RetailSaleMetadata = { salesChannel: "in-store" | "online"; registerId?: string };
type RetailSale = Sale<RetailSaleMetadata>;
// Project Management Module Types
type RetailProjectMetadata = { customerPriority: "high" | "medium" | "low"; targetRevenue?: number };
type RestockTaskStatus = "planned" | "picking" | "in-transit" | "received" | "cancelled";
type RetailTask = Task<RestockTaskStatus, { requiredSkills: string[] }>;
type RetailProject = Project<RetailProjectMetadata>;
// 2. Instantiate and Link Data in a Cohesive Scenario
// Currencies
const USD: Currency = { code: "USD", symbol: "$", precision: 2 };
// Facilities (Logistics)
const mainStore: RetailFacility = {
id: "STORE-NYC-001",
label: "Flagship NYC Store",
category: "retail-store",
status: "open",
area: { unit: "sqm", value: 1500 },
data: { floorAreaSqM: 1500 }, // Specific data for a store facility
metadata: { managerName: "Alice Smith", contactPhone: "+12125551000" },
location: { latitude: 40.748817, longitude: -73.985428, source: "geocoded" },
};
const warehouse: RetailFacility = {
id: "WH-NJ-001",
label: "New Jersey Regional Warehouse",
category: "warehouse-hub",
status: "active",
area: { unit: "sqm", value: 10000 },
data: { floorAreaSqM: 10000 },
metadata: { managerName: "Bob Johnson", contactPhone: "+12015552000" },
location: { latitude: 40.7834, longitude: -74.0089, source: "geocoded" },
};
// Products (Logistics)
const summerDress: RetailProduct = {
id: "DRS-SUMMER-RED-M",
label: "Summer Midi Dress (Red)",
description: "Lightweight red midi dress, 100% cotton.",
data: { unit: "pieces", packaging: "polybag" },
metadata: { brand: "FashionFlow", seasonality: "Summer" },
group: { id: "GRP-DRESSES", label: "Dresses", description: "All dress styles", metadata: { category: "Women's Apparel", department: "Clothing" }, parent: undefined },
type: "fungible", // Though dresses could be non-fungible based on specific SKUs
tags: ["new-arrival", "cotton"],
};
// Stock (Logistics)
const storeDressStock: ProductStock = {
id: "STOCK-DRS-001-NYC",
product: summerDress, // Reference full product object
quantity: 25,
reserved: 5,
acquired: "2025-03-25T10:00:00Z",
storage: mainStore.id, // Reference facility by ID
state: "available",
batch: "SS2025-BATCH-A",
unit: "pieces",
expires: undefined,
metadata: { lastAuditDate: "2025-04-01T15:00:00Z" },
};
const warehouseDressStock: ProductStock = {
id: "STOCK-DRS-001-NJ",
product: summerDress.id, // Reference product by ID string
quantity: 500,
acquired: "2025-03-20T08:00:00Z",
storage: warehouse, // Reference facility by object
state: "available",
batch: "SS2025-BATCH-A",
unit: "pieces",
expires: undefined,
metadata: { lastAuditDate: "2025-04-01T09:00:00Z" },
};
// Stock Movement (Logistics) - Transfer from warehouse to store
const transferMovement: ProductStockMovement = {
id: "MVMT-TRANS-001",
stock: warehouseDressStock.id, // The specific stock batch being moved
type: "transfer-out",
quantity: 100,
unit: "pieces",
source: warehouse.id,
destination: mainStore.id,
timestamp: "2025-04-02T11:00:00Z",
description: "Transfer 100 summer dresses from NJ warehouse to NYC store for peak season.",
metadata: { transferRequest: "REQ-456" }
};
// Financial Accounts (Finance)
const mainSalesAccount: RetailAccount = {
id: "ACC-SALES-NYC",
name: "NYC Store Sales Revenue",
type: "sales-revenue",
// Ledger and balance would be populated by actual transactions
ledger: {
"cash": [], "card": [], "mobile-payment": [], "store-credit": []
},
balance: { "cash": 0, "card": 0, "mobile-payment": 0, "store-credit": 0 },
currency: USD,
groups: undefined,
metadata: { branchId: mainStore.id, openedBy: "finance-team" },
};
// HR - Employee and Organizational Unit
const storeManager: RetailEmployee = {
id: "EMP-SM-001",
data: {
type: "natural",
bio: {
name: { first: "Alice", last: "Smith", other: [] },
dateOfBirth: "1985-05-20T00:00:00Z",
gender: "Female",
placeOfBirth: { country: "USA", metadata: {} },
appearance: []
},
currentRole: "manager",
employeeId: "EMP-SM-001",
address: {
residence: {
street: "123 Main St", city: "New York", state: "NY", postalCode: "10001", country: "USA",
residency: { start: "2020-01-01T00:00:00Z" }, priority: 1, metadata: {}
}, currentLocation: undefined
},
affiliations: [], contact: [], groups: [], history: { finance: { accounts: [], transactions: [], taxInfo: [], income: [], debts: [] }, health: { weight: [], height: [], medicalHistory: [], allergies: [], medications: [], surgeries: [] }, legal: [] },
nationality: [{ country: "USA", status: "citizen", ids: [] }], personality: {}, relationships: [],
},
metadata: {
'data-role': DATA_ROLE_REAL, // Required for Person metadata
hireDate: "2020-01-01T09:00:00Z",
departmentId: "DEPT-NYC-MGT",
status: "active"
},
};
const salesDepartment: RetailOrgUnit = {
id: "DEPT-NYC-SALES",
name: "NYC Sales Department",
parent: mainStore.id, // Parent facility reference
members: [{ person: storeManager.id, role: "Department Head", startDate: "2022-03-01T00:00:00Z" }],
data: { headId: storeManager.id, budgetCode: "SLS-NYC-BUDGET" },
metadata: { locationId: mainStore.id },
};
// Sales - Parties
const supplierParty: RetailParty = {
id: "PARTY-SUPP-FABRIC",
data: {
type: "artificial",
name: "Global Fabric Co.",
industry: "distribution",
incorporation: { type: "Inc", number: "INC98765", date: "2000-01-01T00:00:00Z", country: "USA" },
contact: { email: "info@globalfabric.com", phone: "+1800FABRIC" },
address: { country: "USA", area: "Los Angeles", region: "CA", postalCode: "90001", metadata: {} },
tax: { number: "TAXFABC001", document: "EIN-GFABC001" },
accounts: ["ACC-SUPP-FABRIC-001"], // Assuming supplier has an account
},
metadata: { 'data-role': DATA_ROLE_SALES },
};
const ourCompanyParty: RetailParty = {
id: "PARTY-OUR-RETAIL",
data: {
type: "artificial",
name: "Our Retail Inc.",
industry: "retail",
incorporation: { type: "Inc", number: "INC12345", date: "2015-07-10T00:00:00Z", country: "USA" },
contact: { email: "purchasing@ourretail.com", phone: "+1888RETAIL" },
address: { country: "USA", area: "New York", region: "NY", postalCode: "10001", metadata: {} },
tax: { number: "TAXRETAIL001", document: "EIN-RETAIL001" },
accounts: [mainSalesAccount.id],
},
metadata: { 'data-role': DATA_ROLE_SALES },
};
// Sales - Purchase Order (our company purchasing from supplier)
const purchaseOrder: PurchaseOrder = {
id: "PO-2025-001",
issuer: mainSalesAccount.id, // Our company's account issuing the PO
recipient: "ACC-SUPP-FABRIC-001", // Supplier's account
status: "pending",
date: "2025-04-01T00:00:00Z",
items: [{ description: "Summer Dress (Red, M)", quantity: 100, price: 15.00, total: 1500.00, metadata: { productId: summerDress.id } }],
deliveryDate: "2025-04-15T00:00:00Z",
terms: "Net 30",
notes: undefined, attachments: undefined, references: undefined,
};
// Sales - Customer Sale
const customerSale: RetailSale = {
id: "SALE-CUST-001",
state: "completed",
documents: {
invoices: [{
id: "INV-CUST-001", type: "invoice", issuer: mainSalesAccount.id, recipient: "ACC-CUST-001", status: "completed",
items: [{ description: "Summer Midi Dress (Red)", quantity: 1, price: 59.99, total: 59.99 }],
subtotal: 59.99, taxes: [{ id: "TAX-SALES-001", type: "sales", amount: 5.32, rate: 8.875, description: "NYC Sales Tax" }],
total: 65.31, currency: USD.code, date: "2025-04-03T14:30:00Z",
validity: undefined, terms: undefined, due: undefined, complete: undefined, notes: undefined, attachments: undefined, references: undefined,
}],
receipts: [], quotations: [], proformas: [], credits: [], orders: [], deliveries: [], returns: [], billsOfLading: []
},
buyer: { // Direct object for a casual customer (often just their `Party` data)
id: "PARTY-CUST-JANE",
data: {
type: "natural",
identity: { name: { first: "Jane", last: "Doe" } },
nationality: { country: "USA", id: undefined },
contact: { email: "jane.doe@example.com", phone: undefined },
address: { country: "USA", area: "New York", region: "NY", postalCode: "10001", geolocation: undefined, metadata: {} },
tax: { number: "N/A", document: undefined },
accounts: ["ACC-CUST-001"], // Fictional customer account
occupation: undefined, role: undefined, organization: undefined
},
metadata: { 'data-role': DATA_ROLE_SALES },
},
seller: ourCompanyParty.id,
created: "2025-04-03T14:25:00Z",
status: { payment: "complete", fulfillment: "complete" },
payments: ["TXN-SALES-9876"], // Assuming 'TXN-SALES-9876' is the transaction ID from the basic example
metadata: { salesChannel: "in-store", registerId: "REG-01" },
notes: undefined, attachments: undefined, modified: undefined, completed: undefined,
};
// Connections and implications:
// - `storeDressStock.quantity` would implicitly decrease after `customerSale` is completed (handled by application logic).
// - `mainSalesAccount.balance` would implicitly increase from the `customerSale`'s payment transaction.
// - `purchaseOrder` from `ourCompanyParty` to `supplierParty` might trigger a `ProductStockMovement` for `warehouseDressStock`.
// - `storeManager` is part of `salesDepartment` and their performance/shift logs (`EmployeeShiftLog`) are tracked in HR.
How This Fits Together
This example demonstrates the power of interlinked, generic types:
- Logistics:
ProductStock
tracks inventory (summerDress
) inRetailFacility
locations (mainStore
,warehouse
).ProductStockMovement
logs the movement of stock between these facilities. - Finance:
RetailAccount
s manage financial balances, and their ledger implicitly tracksTransaction
s that result from sales or purchases. - Human Resources:
RetailEmployee
(storeManager
) is aPerson
with HR-specific metadata. They belong to anOrganisationalUnit
(salesDepartment
).EmployeeShiftLog
tracks their time. - Sales:
RetailSale
orchestrates the entire process, referencingRetailParty
for buyer/seller,TransactionDocument
for invoicing, and linking implicitly toProductStock
for inventory. - Generics in Action: Notice how types like
Item
,Stock
,Facility
,Person
, andSale
are specialized with domain-specific type arguments (e.g.,RetailProductData
,StoreCategory
,RetailMedium
) to fit the retail business context without changing the core library interfaces.
This interconnectedness, enforced by TypeScript at compile-time and supportable by anansi
schemas at runtime, ensures data integrity and consistency across your ERP system.
Integration Tips
- API Definitions: Use these types directly to define your REST API request and response payloads. For example, a
POST /inventory/stock
endpoint could acceptProductStock
as its body. - Database Schemas: Map these TypeScript interfaces to your chosen database schemas (e.g., MongoDB documents, PostgreSQL tables). The JSON compatibility facilitates this.
Runtime Validation: Complement TypeScript's compile-time checks with runtime validation using the provided
anansi
schemas. This ensures incoming data (e.g., from external APIs, user input) conforms to the expected structure and constraints (e.g.,quantity
must be positive).import { getDefinition, validate } from '@asaidimu/anansi'; import { SaleSchema } from '@asaidimu/erp-types/schema'; // Import schema directly // Get the Anansi schema definition for Sale const saleDefinition = getDefinition(SaleSchema); // Example: Validate an incoming sales object const incomingSaleData = { id: "SALE-XYZ", state: "completed", // ... rest of the sale data status: { payment: "complete", fulfillment: "complete" }, buyer: { id: "CUST-001", data: { type: "natural", /* ... */ }, metadata: { 'data-role': 'sales' } }, seller: { id: "OUR-BIZ", data: { type: "artificial", /* ... */ }, metadata: { 'data-role': 'sales' } }, created: "2025-01-01T10:00:00Z", payments: ["TXN-123"], documents: { invoices: [] } }; const validationResult = validate(saleDefinition, incomingSaleData); if (validationResult.success) { console.log("Data is valid:", validationResult.data); } else { console.error("Validation errors:", validationResult.errors); }
Domain-Driven Design: Leverage the modularity to build your application around these domains. Each module (
src/finance.ts
,src/logistics.ts
, etc.) can correspond to a microservice or a distinct component in a monolithic architecture.
Best Practices
Declarative Typing:
- Do: Define named types for generic parameters (e.g.,
type MyProductData = { unit: string }
thenItem<MyProductData, ...>
). - Why: Improves readability, reusability, and makes complex type signatures easier to manage.
- Don't: Inline generic parameters directly (e.g.,
Item<{ unit: string }, ...>
).
- Do: Define named types for generic parameters (e.g.,
Separation of Concerns:
- Do: Separate core data (
data
), optional context (metadata
), and finite states (union types). - Why: Keeps concerns distinct, allowing
data
to hold essential properties,metadata
for flexible extensions, and union types for strict state management. - Don't: Lump all properties into a single, un-categorized object.
- Do: Separate core data (
Thoughtful Union Types:
- Do: Use union types for finite, well-defined states or categories (e.g.,
type StockStatus = "available" | "sold" | "restocking"
). - Why: Clarifies constraints, prevents invalid values, and improves code comprehension.
- Don't: Rely on raw
string
types where a limited set of values is expected.
- Do: Use union types for finite, well-defined states or categories (e.g.,
Referencing by ID or Object:
- Do: Utilize the
string | Type
pattern (e.g.,storage: string | Facility
). - Why: Provides flexibility for API design (send just ID) and internal graph traversal (load full object).
- Don't: Exclusively use
string
if you sometimes need to embed the full object, or exclusively use the full object if you only need the ID.
- Do: Utilize the
Anti-Patterns to Avoid
- Ad-Hoc Generics: Creating types like
const x: Stock<Item<{ unit: string }, { brand: string }>, ...>
is cumbersome and hinders reusability. Always define named types for your generic arguments. - Ignoring Union Types: Allowing any
string
value where a specific set of statuses is expected (e.g.,status: string
instead ofstatus: "active" | "inactive"
). - Overloading Domains: Adding fields that belong to another domain (e.g.,
price
directly toStock
instead ofTransaction
or a separateProductPrice
interface). This violates modularity. - Mixing Runtime Logic with Types: Never embed validation logic or business rules directly into these interfaces. They are purely for data structure definition.
Project Architecture
This project does not implement a runtime application. Instead, it provides a structured collection of TypeScript interfaces and their corresponding anansi
schema definitions that define the data models for an ERP system.
Core Components
The "core components" of this library are its TypeScript interface definitions and their corresponding anansi
schema definitions, organized logically by business domain within the src/
directory.
src/types/
: Contains pure TypeScript interfaces (.ts
files), providing compile-time type safety and defining the structural contracts for all ERP data entities.src/schema/
: Containsanansi
schema definitions, which are JavaScript/TypeScript objects that formally describe the structure and constraints of the data. These are crucial for runtime validation, API documentation generation, and dynamic form rendering.common.ts
(in bothtypes
andschema
): Holds fundamental, cross-cutting types and their schemas (e.g.,ISOStringDate
,Address
,Geolocation
,Timespan
), ensuring consistency across multiple modules.index.ts
(root level): Serves as the public API, re-exporting all primary interfaces fromsrc/types
and all schema definitions fromsrc/schema
, making them easy to import from the@asaidimu/erp-types
package.
Data Flow
As a types-only library, @asaidimu/erp-types
does not define runtime data flow or application logic. Instead, it provides the blueprint for how data should be structured and how different entities relate to each other.
- Cross-Module Referencing: Types frequently reference entities from other modules using either their
id: string
or the full object (e.g.,person: string | Person<any, any>
). This pattern facilitates the creation of interconnected graphs of business data, where an entity in one domain (e.g., anEmployee
in HR) can be referenced in another (e.g., anAssignment
in Project Management). - Generics for Flexibility: Extensive use of generics (
<TData>
,<TMetadata>
,<TState>
) allows consumers to define specific data shapes for their unique business contexts while adhering to the library's core structure. This means the "data flow" is defined by your application's implementation, guided by these flexible type contracts.
For example, a Sale
type in the sales
module might implicitly "flow" data to Account
types in the finance
module (for revenue recording) and Stock
types in the logistics
module (for inventory reduction). The library dictates what that data looks like, but your application code handles how it is moved and processed.
Extension Points
The library is designed for maximum extensibility:
- Named Generics: By using named generic type parameters (e.g.,
Person<PersonData, DataRole, PersonMetadata>
), consumers can inject their own specific data shapes without modifying the base interfaces. This allows for deep customization while retaining compatibility. - Metadata Fields: Most core interfaces include a
metadata?: Record<string, unknown>
field (or similar generic types). This provides a flexible escape hatch for adding arbitrary, context-specific properties to any entity without extending the core type itself. - Union Types as Customization: For fields like
type
orstatus
, many interfaces usestring
or a default generic type (TState extends string
). This allows developers to define union types (e.g.,type MyOrderStatus = "pending" | "shipped" | "delivered"
) in their own projects and pass them as generic arguments, tailoring the schema to their specific state machines. anansi
Schemas: The modular nature ofanansi
schemas means you can extend or compose existing schemas to define more complex validation rules or to generate specialized forms/documentation without altering the core library.
This design philosophy means that rather than forking the library for minor adjustments, users can extend and specialize the types and their validation rules directly within their own codebase.
Development & Contributing
We welcome contributions to @asaidimu/erp-types
! Whether it's adding new modules, improving existing type definitions, or enhancing documentation, your input is valuable.
Development Setup
To set up the project for local development:
- Clone the repository:
git clone https://github.com/asaidimu/erp-types.git cd erp-types
- Install dependencies:
This project uses
bun
for package management and scripts.
(If you don't have Bun, you can usebun install
npm install
, but Bun is recommended for script execution speed.)
Available Scripts
The package.json
defines several useful scripts for development:
bun ci
: Installs project dependencies.bun clean
: Removes thedist/
directory, cleaning up previous build artifacts.bun prebuild
: Executesbun clean
and then runs a customsync-package.ts
script (not provided, but implied to preparedist.package.json
).bun build
: Compiles TypeScript files fromindex.ts
todist/
in CommonJS and ES Module formats, and generates TypeScript declaration files (.d.ts
). This command internally usestsup
.bun postbuild
: CopiesREADME.md
andLICENSE.md
to thedist/
directory, and movesdist.package.json
todist/package.json
for proper package publishing.
To build the project:
bun run build
Testing
This library primarily consists of TypeScript interfaces and schema definitions, and its "testing" largely relies on:
- TypeScript Compiler Checks: The
bun run build
command (which internally usestsup
andtypescript
) will perform comprehensive type checking across all files, ensuring that all interfaces are correctly defined and used. Any type errors indicate a problem in the definitions. - ESLint: The project uses ESLint with
@typescript-eslint/eslint-plugin
for code quality and consistency. While not explicitly defined as a separatelint
script inpackage.json
, runningeslint src/
would apply these checks to enforce style and identify potential issues.
We encourage contributors to ensure their changes pass TypeScript compilation without errors and adhere to the project's coding style and eslint
rules.
Contributing Guidelines
We appreciate your contributions! Please follow these guidelines:
- Fork the Repository: Start by forking the
asaidimu/erp-types
repository on GitHub. - Create a Branch: Create a new branch for your feature or bug fix (e.g.,
feature/add-crm-types
,fix/logistics-typo
). - Code Changes:
- Pure Types: This library is strictly for TypeScript interfaces (
src/types
) andanansi
schema definitions (src/schema
). Do not add any runtime logic, utility functions, or data validation implementations directly into these files. - Consistency: Adhere to the existing coding style and naming conventions.
- Generics & Extensibility: Favor using generics and
metadata
fields for flexible extensions rather than hardcoding specific values. - Documentation: Add JSDoc comments to new interfaces, types, properties, and schema definitions, explaining their purpose, usage, and any constraints.
- Atomic Commits: Make small, focused commits with clear, descriptive messages following conventional commits (e.g.,
feat(sales): add sales order interface
,fix(logistics): correct typo in stock field
).
- Pure Types: This library is strictly for TypeScript interfaces (
- Build & Verify: Run
bun run build
to ensure your changes compile without errors. - Submit a Pull Request:
- Open a Pull Request (PR) to the
main
branch of the original repository. - Provide a clear title and description for your PR, summarizing your changes and their purpose.
- Reference any related issues (e.g.,
Fixes #123
,Closes #456
).
- Open a Pull Request (PR) to the
Issue Reporting
If you encounter any bugs, have feature requests, or suggestions for improvement, please open an issue on GitHub:
- Report Bugs: Provide a clear, concise description of the bug, steps to reproduce it, expected behavior, and your environment details (TypeScript version, Node.js/Bun version).
- Request Features: Describe the feature you'd like to see, explaining its use case and why it would be beneficial to the library.
- General Questions: Feel free to open an issue for questions about usage or design decisions.
All issues can be reported at the official GitHub repository: https://github.com/asaidimu/erp-types/issues.
Additional Information
Troubleshooting
TypeScript Errors:
Type 'X' is not assignable to type 'Y'
: This indicates a mismatch between your data structure and the expected interface. Double-check the generic parameters you're using.- Missing Properties: Ensure all
required
properties in an interface are provided. - Excess Properties: If you're encountering
Object literal may only specify known properties
errors, it means your object has properties not explicitly defined in the interface. Utilizemetadata
fields or extend interfaces if you need to add custom properties. - Dependency Issues: If
bun install
ornpm install
fails, clear your package manager cache (bun cache clean
ornpm cache clean --force
) and try again.
Build Failures:
- Ensure TypeScript is installed globally or locally (
typescript
indevDependencies
). - Check your
tsconfig.json
for correct paths and compiler options. - Verify that
tsup
is installed and correctly configured.
- Ensure TypeScript is installed globally or locally (
FAQ
Q: Can I use this library with JavaScript? A: While the library itself is written in TypeScript and provides type definitions, you can use it in a JavaScript project. However, you won't benefit from the compile-time type safety. Your IDE (if it supports TypeScript definitions) might still provide type hints. For runtime validation in JavaScript, you can leverage the
anansi
schemas.Q: Does this library include any runtime logic or validation? A: No,
@asaidimu/erp-types
is primarily a collection of TypeScript interfaces. It defines data shapes, not behavior. While it providesanansi
schemas for validation, you'll need to implement your own runtime logic, data processing, and business rules.Q: How do I add custom fields to an existing type? A: Many interfaces include a
metadata?: Record<string, unknown>
field (or a more specific generic type for metadata). This is the recommended way to add custom, application-specific data without modifying the core library. If you need to add a fundamental, non-optional field to a core type, consider opening an issue or a pull request to discuss its inclusion in the library.Q: Can I use this with frameworks like NestJS, Express, or React? A: Absolutely! These types are framework-agnostic. You can use them to define DTOs (Data Transfer Objects) in your NestJS/Express APIs, model state in React components, or define data structures for any part of your application. The
anansi
schemas can also be integrated into API middleware for request body validation.
Changelog & Roadmap
For a detailed history of changes and new features, please refer to the CHANGELOG.md file.
Future Extensions (Roadmap):
Based on the strategic vision, potential future extensions for this ERP type library include:
- Machine Learning Integration: Predictive analytics models for inventory, sales forecasting, and resource planning.
- Blockchain Support: Immutable transaction records for critical business processes, enhancing transparency and auditability.
- IoT Connectivity: Direct integration with smart devices and sensors for real-time data collection in logistics and manufacturing.
- Augmented Reality (AR): Visual interfaces for warehouse management, maintenance, and field operations.
- Voice Interfaces: Natural language processing capabilities for hands-free operation and improved user interaction.
- Containerized Deployment: Enhanced types and considerations for microservice architectures with Kubernetes orchestration, facilitating cloud-native deployments.
License
This project is licensed under the MIT License. See the LICENSE.md file for full details.
Acknowledgments
Developed by asaidimu. Inspired by the need for robust, flexible, and type-safe data models in enterprise systems.
Last Updated: March 2025