Projects: classes as entities, and the sidebar fits short windows
check / check (push) Canceled after 0s

A project is a class with facts (description, status, kind, party,
dates, links, notes) in one project.set event behind the format
feature projects. Four tools (books.add_project, update_project,
projects, project); documents and decisions link to projects; B-37:
on-spec costs are expensed as incurred, repayment is revenue, and the
unrecovered cost is never below zero (Kani). Viewer: a Projects list
and a project page with its figures, by month and by account. The
sidebar measures its footer instead of reserving a fixed 172 px.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-22 03:50:52 -06:00
1 parent 0495726001
commit 321b94562a
71 files changed
+7446 -217

No files matched your search

+67 -3
View File
@@ -242,8 +242,25 @@ pub fn build(dir: &Path, today: NaiveDate) -> Result<(), StoreError> {
Some("depreciation"),
)?;
let bank_fees = acct(&mut s, "6900", "Bank Fees", Expense, None)?;
let client_class = s.add_class("Client work")?;
let template_class = s.add_class("Templates")?;
// The projects (classes with their facts, rules.md B-37).
let client_class = s.add_project(
"Client work",
&ledger_core::ProjectFacts {
description: Some("Design work for the studio's clients".into()),
kind: Some(ledger_core::ProjectKind::Client),
..Default::default()
},
)?;
let template_class = s.add_project(
"Templates",
&ledger_core::ProjectFacts {
description: Some("Notion and Figma templates sold on Gumroad".into()),
kind: Some(ledger_core::ProjectKind::Internal),
started_on: Some(start),
links: vec!["https://juniper.example/templates".into()],
..Default::default()
},
)?;
// Each vendor's 1099 decision, as the owner made it when they were
// first paid; the card is a payment method, so what was charged to it
@@ -282,6 +299,21 @@ pub fn build(dir: &Path, today: NaiveDate) -> Result<(), StoreError> {
.iter()
.map(|n| s.add_customer(n))
.collect::<Result<_, _>>()?;
// Work on spec for a customer, hoping they adopt it: its costs are
// expenses, and nothing is receivable (rules.md B-37 item 4).
let tidepool_started = start + Months::new(13);
let tidepool = s.add_project(
"Tidepool app",
&ledger_core::ProjectFacts {
description: Some(
"An events app built for Tidepool Records ahead of a contract".into(),
),
kind: Some(ledger_core::ProjectKind::OnSpec),
party: s.ledger().party_of_customer(customers[3]),
started_on: Some(tidepool_started),
..Default::default()
},
)?;
// The party directory: who they are, beyond the vendor and customer
// records (roles, W-9 facts with only the TIN's last four, sites).
@@ -827,7 +859,12 @@ pub fn build(dir: &Path, today: NaiveDate) -> Result<(), StoreError> {
),
)),
Some(riley),
Some(client_class),
// The second milestone builds the Tidepool app.
Some(if month_index == 13 {
tidepool
} else {
client_class
}),
)?;
}
}
@@ -962,6 +999,33 @@ pub fn build(dir: &Path, today: NaiveDate) -> Result<(), StoreError> {
)?;
s.link_attachment(contract_doc, Subject::Party(sam_party))?;
s.link_attachment(contract_doc, Subject::Company)?;
// The Tidepool app's pitch, linked to it, and the decision to build it
// on spec.
let (pitch, _, _) = s.file_document(
"other",
"Tidepool app pitch.pdf",
"application/pdf",
&receipt_pdf(
"Tidepool app pitch",
&[
"Juniper Studio LLC for Tidepool Records".into(),
"An events app: shows, tickets, a mailing list".into(),
format!("Sent {}", tidepool_started.format("%B %-d, %Y")),
],
),
Some("The pitch"),
)?;
s.link_attachment(pitch, Subject::Project(u64::from(tidepool)))?;
s.record_decision(&ledger_core::conventions::NewDecision {
subject: "The Tidepool app".into(),
decision_md: "Build the Tidepool app on spec; bill only if they adopt it.".into(),
decided_by: vec!["Alex Rivera".into()],
decided_on: tidepool_started,
evidence_attachment: Some(pitch),
supersedes: None,
citation: None,
projects: vec![tidepool],
})?;
// Statements and reconciliations: savings reconciled through last
// month; checking through the month before (its first reconciliation