Transparency

These documents keep us focused, remind us why we exist and show all that Shasta Trades is organized, mission focused, and transparent.

What is a database, what is SQL and what is a database schema?

  • A database is an organized collection of information
  • SQL is the most popular database programming language
  • A database schema defines how database information is organized (tables, rows, indexes, etc.)
    • All our data goes into an encrypted SQL database
    • The architecture of this database is a schema

Why do we need this detailed of a schema?

  • We are running a trades school, a construction company and a grant-funded nonprofit all at once
  • Grantors don't just give money and walk away
    • They love financial reports and this schema helps us organize our data so we may provide these reports
  • Instead of using QuickBooks for money, Airtable for clients, and Google Sheets for hours, everything lives in one encrypted SQL database
  • Example queries we may run thanks to this schema:
    • Identify and restrict specific grant funds to specific liabilities
    • Tell an apprentice how many hours they have left until they reach their 4-year CSLB requirement
    • Identify how many low-income, medium-income and/or senior citizen households we've helped this quarter, for grants that love helping specific cohorts of our community

Person

Store all people in our system (students, mentors, customers, Trustees, Board members, employees, vendors, etc.)

FieldTypeNotes
idINTEGERPK, AI
firstNameTEXTNOT NULL
lastNameTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1

Contact

Store contact details for each Person

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERUNIQUE INDEX, NOT NULL, FK → Person(id), CASCADE DELETE
emailTEXTUNIQUE INDEX, NOT NULL
emailVerifiedBOOLEANDEFAULT = 0
sendNewsletterBOOLEANDEFAULT = 1
sendJobOpportunityEmailsBOOLEANDEFAULT = 0
phoneNumberTEXTNULLABLE
phoneNumberVerifiedBOOLEANDEFAULT = 0
sendJobOpportunityTextsBOOLEANDEFAULT = 0

Session

Stores authentication details between a Person and our application

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERINDEX, NOT NULL, FK → Person(id), CASCADE DELETE
expiresAtDATETIMENOT NULL
createdAtDATETIMENOT NULL
ipAddressTEXTNOT NULL

MagicToken

Before a Session is created we send a Person an email w/ a MagicToken (passwordless / magic link authentication)

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERINDEX, NOT NULL, FK → Person(id), CASCADE DELETE
tokenHashTEXTINDEX, NOT NULL
expiresAtDATETIMENOT NULL
usedBOOLEANNOT NULL, DEFAULT = 0

ContactUsMessage

Store messages that are filled out with our Contact Us website form

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERNOT NULL, FK → Person(id), CASCADE DELETE
messageTEXTNOT NULL
createdAtDATETIMENOT NULL, DEFAULT = NOW

Trade

Trades lookup table

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1

Job

Store all work projects

FieldTypeNotes
idINTEGERPK, AI
statusIdINTEGERINDEX, NOT NULL, FK → JobStatus(id)
descriptionTEXTNULLABLE
addressTEXTNOT NULL
createdAtDATETIMENOT NULL, DEFAULT = NOW

Job__Trade

Junction table between Job & Trade

FieldTypeNotes
idINTEGERPK, AI
jobIdINTEGERNOT NULL, FK → Job(id), CASCADE DELETE
tradeIdINTEGERINDEX, NOT NULL, FK → Trade(id)
--UNIQUE(jobId, tradeId)

Job__Client

Junction table between Job & Person (Client)

FieldTypeNotes
idINTEGERPK, AI
jobIdINTEGERNOT NULL, FK → Job(id), CASCADE DELETE
clientIdINTEGERINDEX, NOT NULL, FK → Person(id), CASCADE DELETE
--UNIQUE(jobId, clientId)

JobStatus

Job status lookup table

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
descriptionTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1

JobLead

Store entries from our service request (job lead) form

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERNOT NULL, FK → Person(id), CASCADE DELETE
jobIdINTEGERUNIQUE INDEX, NULLABLE, FK → Job(id), CASCADE DELETE
statusIdINTEGERINDEX, NOT NULL, FK → LeadStatus(id)
descriptionTEXTNOT NULL
createdAtDATETIMENOT NULL, DEFAULT = NOW

LeadStatus

Lead status lookup table

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1

Trade__JobLead

Junction table between Trade & JobLead

FieldTypeNotes
idINTEGERPK, AI
jobLeadIdINTEGERNOT NULL, FK → JobLead(id), CASCADE DELETE
tradeIdINTEGERINDEX, NOT NULL, FK → Trade(id)
--UNIQUE(tradeId, jobLeadId)

StaffLead

Store entries from our staff interest form

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERNOT NULL, FK → Person(id), CASCADE DELETE
statusIdINTEGERNOT NULL, FK → LeadStatus(id)
positionIdINTEGERNOT NULL, FK → StaffPosition(id)
createdAtDATETIMENOT NULL, DEFAULT = NOW

StaffPosition

Staff position lookup table

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1
isHiringBOOLEANNOT NULL, DEFAULT = 1

Person__StaffPosition

Junction table between Person & StaffPosition that also tracks the employment time and potential reason for ending the position

FieldTypeNotes
idINTEGERPK, AI
personIdINTEGERNOT NULL, FK → Person(id), CASCADE DELETE
positionIdINTEGERINDEX, NOT NULL, FK → StaffPosition(id)
endReasonIdINTEGERNULLABLE, FK → StaffEndReason(id)
startDateDATETIMENOT NULL
endDateDATETIMENULLABLE
--UNIQUE(personId, positionId)

StaffEndReason

Staff end reason lookup table

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1

Objective

Stores objectives on our Kanban

FieldTypeNotes
idINTEGERPK, AI
columnIdINTEGERINDEX, NOT NULL, FK →ObjectiveColumn(id)
createdByINTEGERNOT NULL, FK →Person(id), CASCADE DELETE
titleTEXTUNQUE INDEX, NOT NULL
descriptionTEXT
orderDECIMALINDEX, NOT NULL
createdAtDATETIMENOT NULL, DEFAULT = NOW

Objective__Assignee

Junction table between Objective & Person

FieldTypeNotes
idINTEGERPK, AI
objectiveIdINTEGERNOT NULL, FK → Objective(id), CASCADE DELETE
personIdINTEGERINDEX, NOT NULL, FK → Person(id), CASCADE DELETE
--UNIQUE(objectiveId, personId)

ObjectiveColumn

Objective column (on Kanban) lookup table

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1

ObjectiveTag

Objective tag lookup table (more specific then ObjectiveColumn)

FieldTypeNotes
idINTEGERPK, AI
valueTEXTNOT NULL
isActiveBOOLEANNOT NULL, DEFAULT = 1
orderINTEGERNOT NULL

Objective__Tag

Junction table between Objective & ObjectiveTag

FieldTypeNotes
idINTEGERPK, AI
objectiveIdINTEGERNOT NULL, FK → Objective(id), CASCADE DELETE
tagIdINTEGERINDEX, NOT NULL, FK → ObjectiveTag(id)
--UNIQUE(objectiveId, tagId)

ObjectiveComment

Store Objective comments

FieldTypeNotes
idINTEGERPK, AI
objectiveIdINTEGERINDEX, NOT NULL, FK → Objective(id), CASCADE DELETE
createdByINTEGERNOT NULL, FK →Person(id), CASCADE DELETE
valueTEXTNOT NULL
createdAtDATETIMENOT NULL, DEFAULT = NOW

ObjectiveComment__Assignee

Junction table between ObjectiveComment & Person. If someone is assigned to an Objective then DO NOT store an entry for them here. ObjectiveComment__Assignee is for notifying people about an ObjectiveComment that are NOT assigned to an Objective when we'd love for them to know about a comment.

FieldTypeNotes
idINTEGERPK, AI
commentIdINTEGERNOT NULL, FK → ObjectiveComment(id), CASCADE DELETE
personIdINTEGERINDEX, NOT NULL, FK → Person(id), CASCADE DELETE
--UNIQUE(commentId, personId)