This is another revision of my Psycho Productions database, this time refactored from MySQL to PostgreSQL, after I found out about some of the shortcomings of MySQL.
The following is a (simplified) version of relationships in a flowchart. The way I used arrows is: where the arrow originates = FK and where it points to = PK. Only one relationship is optional (the dashed arrow) as a project is allowed to not be invoiced (e.g., if it is declined) and an invoice is allowed not to be a project (e.g., a bill or something like that).
If you wish to see the YUML diagram, it is here.
PS: PostgreSQL automatically creates indexes on PKs, hence not explicitly defining indexes. If you feel other indexes are needed, I'm open to suggestions!
Graphical illustration:
Following is the full schema DDL code. I would appreciate if you could please provide any advice on design or any other aspect.
DROP SCHEMA IF EXISTS PsychoProductions;
CREATE SCHEMA PsychoProductions;
SET SEARCH_PATH = PsychoProductions;
--
-- Create table with standard values
-- to be referenced to by other tables
-- And insert some values in those tables
--
-- Person types
CREATE TABLE PersonRole(
PersonRoleId SERIAL PRIMARY KEY,
PersonRoleName TEXT
);
INSERT INTO PersonRole
(PersonRoleName)
VALUES
('Staff'),
('Partner'),
('Customer'),
('Vendor'),
('Session musician')
;
-- Billing methods
CREATE TABLE BillingMethod(
BillingMethodId SERIAL PRIMARY KEY,
BillingMethod TEXT
);
INSERT INTO BillingMethod
(BillingMethod)
VALUES
('Unassigned'),
('Net 30'),
('Net 15'),
('Cash on delivery'),
('Cash with order')
;
-- Product types
CREATE TABLE Product(
ProductId SERIAL PRIMARY KEY,
ProductName TEXT,
ProductCost DECIMAL (8,2),
ProductStandard BOOLEAN DEFAULT True, -- Set to False if ad hoc project type
ProductTaxable BOOLEAN DEFAULT False -- No tax product if not physical good
);
INSERT INTO Product
(ProductName, ProductCost, ProductStandard, ProductTaxable)
VALUES
('Basic musical arrangement (3 or fewer)', 30, True, False),
('Basic musical arrangement (4 or more)', 25, True, False),
('Advanced musical arrangement (3 or fewer)', 50, True, False),
('Advanced musical arrangement (4 or more)', 40, True, False),
('Instrumental leasing (3 or fewer)', 25, True, False),
('Instrumental leasing (4 or more)', 20, True, False),
('Instrumental leasing (NAPH 3 or more)', 20, True, False),
('Graphic design (album sleeve)', 80, True, False),
('Graphic design (full CD & sleeve)', 150, True, False),
('Graphic design (full CD, sleeve & booklet)', 200, True, False),
('Graphic design (flyers)', 40, True, False),
('Graphic design (t-shirt)', 30, True, False),
('Graphic design (logo, sticker, small items)', 25, True, False),
('Rush uplift charge (Basic project)', 10, True, False),
('Rush uplift charge (Advanced project)', 20, True, False)
;
-- Invoice Status types
CREATE TABLE InvoiceStatus(
InvoiceStatusID SERIAL PRIMARY KEY,
InvoiceStatus TEXT
);
INSERT INTO InvoiceStatus
(InvoiceStatus)
VALUES
('Open'),
('Paid'),
('Partially Paid'),
('Cancelled')
;
-- Transaction types
CREATE TABLE TransactionType(
TransactionTypeId SERIAL PRIMARY KEY,
TransactionType TEXT
);
INSERT INTO TransactionType
(TransactionType)
VALUES
('Debit'),
('Credit')
;
-- Address types
CREATE TABLE AddressType(
AddressTypeId SERIAL PRIMARY KEY,
AddressType TEXT
);
INSERT INTO AddressType
(AddressType)
VALUES
('Unique'),
('Physical'),
('Shipping'),
('Billing'),
('Mailing')
;
-- Phone types
CREATE TABLE PhoneType(
PhoneTypeId SERIAL PRIMARY KEY,
PhoneType TEXT
);
INSERT INTO PhoneType
(PhoneType)
VALUES
('Mobile'),
('Business'),
('Home'),
('Fax'),
('Pager')
;
-- Email types
CREATE TABLE EmailType(
EmailTypeId SERIAL PRIMARY KEY,
EmailType TEXT
);
INSERT INTO EmailType
(EmailType)
VALUES
('Business'),
('Personal')
;
--
-- Create master tables which will contain actual business data
--
/* CREATE ALL CORE TABLES RELATED TO PERSONS */
-- This table will contain primary person information
CREATE TABLE Person(
PersonId SERIAL PRIMARY KEY,
PersonRoleId INT NOT NULL DEFAULT 3
REFERENCES PersonRole(PersonRoleId),
FirstName TEXT NOT NULL,
LastName TEXT,
Organization TEXT,
Website TEXT,
DefaultBillingMethodId INT NOT NULL DEFAULT 1
REFERENCES BillingMethod(BillingMethodId),
Active BOOLEAN DEFAULT True,
CreationDate TIMESTAMP DEFAULT NOW()
);
-- Addresses here
CREATE TABLE Address(
AddressId SERIAL PRIMARY KEY,
PersonId INT NOT NULL -- One-to-many relationship
REFERENCES Person(PersonID),
AddressTypeId INT NOT NULL DEFAULT 1 -- Unique
REFERENCES AddressType(AddressTypeId),
Address TEXT,
City TEXT,
State TEXT,
ZipCode TEXT
);
-- Phone numbers here
CREATE TABLE Phone(
PhoneId SERIAL PRIMARY KEY,
PersonId INT NOT NULL -- One-to-many relationship
REFERENCES Person(PersonId),
PhoneNumber TEXT NOT NULL,
PhoneTypeId INT NOT NULL DEFAULT 1 -- Mobile
REFERENCES PhoneType(PhoneTypeId)
);
-- Emails here
CREATE TABLE Email(
EmailId SERIAL PRIMARY KEY,
PersonId INT NOT NULL -- One-to-many relationship
REFERENCES Person(PersonId),
EmailAddress VARCHAR(50) NOT NULL,
EmailTypeId INT NOT NULL DEFAULT 1 -- Business
REFERENCES EmailType(EmailTypeId)
);
/* CREATE ALL TABLES RELATED TO PROJECTS */
-- This table will contain primary project information
CREATE TABLE Project(
ProjectId SERIAL PRIMARY KEY,
RequestPersonId INT NOT NULL,
FOREIGN KEY (RequestPersonID)
REFERENCES Person(PersonID),
AssignPersonId INT,
FOREIGN KEY (AssignPersonID)
REFERENCES Person(PersonID),
ProjectName VARCHAR(200) NOT NULL,
Description TEXT,
OrderDate DATE NOT NULL,
DueDate DATE,
CompleteDate DATE
);
-- Line number of product in project (tied to transactions)
/* CREATE ALL TABLES RELATED TO MONEY */
-- Invoices here
CREATE TABLE Invoice(
InvoiceId SERIAL PRIMARY KEY,
ProjectId INT NULL -- Not all invoices will be tied to a project
REFERENCES Project (ProjectId),
InvoiceByPersonId INT NOT NULL
REFERENCES Person(PersonId),
BillToPersonId INT NOT NULL
REFERENCES Person(PersonId),
BillToAddressId INT NOT NULL
REFERENCES Address(AddressId),
ShipToAddressId INT NULL -- Most invoiced products are not physical products
REFERENCES Address(AddressId),
InvoiceStatusId INT NOT NULL DEFAULT 1 -- Open
REFERENCES InvoiceStatus (InvoiceStatusId),
InvoiceDate DATE NOT NULL,
InvoicePaidDate DATE NULL
);
CREATE TABLE InvoiceDetail(
InvoiceDetailId SERIAL PRIMARY KEY,
InvoiceId INT NOT NULL
REFERENCES Invoice(InvoiceId),
InvoiceSequenceId INT NOT NULL,
ProductId INT NOT NULL
REFERENCES Product(ProductId),
Quantity INT NOT NULL DEFAULT 1,
TaxableRate DECIMAL(5,2)
);
-- Monetary transactions will be logged here
CREATE TABLE AccountingTransaction(
TransactionId SERIAL PRIMARY KEY,
TransactionTypeId INT NOT NULL
REFERENCES TransactionType(TransactionTypeId),
ProjectId INT NULL -- Not all transactions will be tied to a project
REFERENCES Project (ProjectId),
InvoiceId INT NULL -- Ditto for invoice
REFERENCES Invoice(InvoiceId),
InvoiceSequenceId INT NULL, -- Ditto
PaidByPersonId INT NOT NULL
REFERENCES Person(PersonId),
PaidToPersonId INT NOT NULL
REFERENCES Person(PersonId),
TransactionDate DATE NOT NULL,
TransactionNote VARCHAR(1000)
);
Execution:
Query returned successfully with no result in 388 ms.