Skip to content

feat(op-work-packages): WorkPackage model foundation#64

Merged
AdaWorldAPI merged 1 commit into
mainfrom
claude/beautiful-gates-dJo0u
Jul 1, 2026
Merged

feat(op-work-packages): WorkPackage model foundation#64
AdaWorldAPI merged 1 commit into
mainfrom
claude/beautiful-gates-dJo0u

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

What

Fills the op-work-packages stub with the WorkPackage entity — OpenProject's headline unit of work (the Issue-equivalent), converging with Redmine's Issue on the canonical concept project_work_item (0x0102).

Mirrors the op-journals house pattern (plain model struct + newtype + new/builder + tests) and wires into op-core's trait system:

  • WorkPackage — the core Rails columns: subject, description (Formattable), project_id / type_id / status_id / priority_id / author_id FKs, optional assigned_to_id / responsible_id / parent_id, start_date / due_date, estimated_hours, done_ratio, lock_version, timestamps.
  • DoneRatio newtype — whole-percent 0..=100, clamped on construction.
  • new(required fields) + with_* builder; domain helpers is_complete, is_child, has_assignee, is_overdue(today).
  • impls op_core Identifiable / Timestamped / ProjectScoped / Lockable / Entity (TABLE_NAME = "work_packages", TYPE_NAME = "WorkPackage").

Tests

6 unit tests: defaults, DoneRatio clamp, builder relations, is_overdue logic (past-due ∧ incomplete only), the op-core trait impls, and a JSON round-trip.

Gate

cargo +1.95 fmt --check clean; cargo clippy -p op-work-packages reports 0 warnings in this crate's own code; test -p op-work-packages 6 pass.

⚠️ clippy -p op-work-packages -D warnings transitively surfaces pre-existing op-core lints (should_implement_trait in pagination.rs, an unused import / unreachable patterns) that are already on mainop-core is untouched by this PR. Happy to clean those up in a separate op-core PR if you'd like.

Branch note

Follows the merged #63; branch restarted from the new main (6bc77fd) per the merged-branch rule, single follow-up commit on top, no overlap with the walled odoo-rs-transcode branch.

Follow-ups: op-canon class_id wiring on WorkPackage; the write-model + CreateService (mirroring journal_service).

🤖 Generated with Claude Code


Generated by Claude Code

Fill the op-work-packages stub with the WorkPackage entity — OpenProject's
headline unit of work (Issue-equivalent), converging with Redmine's Issue
on canonical concept project_work_item (0x0102).

Mirrors the op-journals house pattern (plain model struct + newtype +
new/builder + tests) and wires into op-core's trait system:

- WorkPackage struct: the core Rails columns — subject, description
  (Formattable), project/type/status/priority/author FKs, optional
  assignee/responsible/parent, start/due dates, estimated_hours,
  done_ratio, lock_version, timestamps.
- DoneRatio newtype: whole-percent 0..=100, clamped on construction.
- new(required fields) + with_* builder; domain helpers is_complete,
  is_child, has_assignee, is_overdue(today).
- impls op_core Identifiable / Timestamped / ProjectScoped / Lockable /
  Entity (TABLE_NAME="work_packages", TYPE_NAME="WorkPackage").

6 unit tests (defaults, DoneRatio clamp, builder, is_overdue logic, trait
impls, JSON round-trip). fmt clean; `cargo clippy -p op-work-packages`
reports 0 warnings in this crate's own code; green on toolchain 1.95.
(`-D warnings` transitively surfaces pre-existing op-core lints already on
main — op-core untouched here, out of scope.)

Follow-ups: op-canon class_id wiring; WorkPackage write-model + service.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 923b173a87

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

/// Status FK (New / In progress / Closed / …).
pub status_id: Id,
/// Priority FK (Low / Normal / High / …).
pub priority_id: Id,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow priority-less work packages

When a work package is created or read without a priority, this model cannot represent it because priority_id is a required Id. The existing database row uses Option<i64> for priority_id (crates/op-db/src/work_packages.rs:21), and the API create DTO accepts priority_id: Option<Id> (crates/op-api/src/handlers/work_packages.rs:291), so persisted NULL-priority rows would either fail conversion/deserialization into this new foundation model or force callers to invent a synthetic priority id. Make this field and the constructor parameter optional to match the existing data shape.

Useful? React with 👍 / 👎.

/// not yet complete. A WP with no due date, or one already done, is never
/// overdue.
pub fn is_overdue(&self, today: NaiveDate) -> bool {
self.due_date.is_some_and(|due| due < today) && !self.is_complete()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Base overdue checks on open status

For a work package whose status is closed but whose done_ratio is less than 100, this helper reports it as overdue. Existing query code treats overdue work packages as past-due and open (crates/op-queries/src/builder.rs:547-552), while status closedness is tracked separately from the percentage (crates/op-models/src/status.rs:32-49), so closed/done state is not equivalent to done_ratio == 100. Consumers using this helper for overdue badges or notifications will surface closed items as overdue unless the check uses/accepts status closedness instead of is_complete().

Useful? React with 👍 / 👎.

@AdaWorldAPI AdaWorldAPI merged commit bbc6bf3 into main Jul 1, 2026
AdaWorldAPI pushed a commit that referenced this pull request Jul 1, 2026
Address the two codex P2 review comments on #64.

1. priority_id is nullable in OpenProject (op-db WorkPackageRow carries
   Option<i64>, the op-api DTO Option<Id>), but the foundation model
   required it — a NULL-priority row couldn't be represented. Make
   priority_id Option<Id>; drop it from the required new(...) params (now
   subject, project_id, type_id, status_id, author_id) and add
   with_priority().

2. is_overdue keyed off done_ratio == 100, but overdue-ness tracks OPEN
   status, not completion (op-queries overdue() == overdue().open(); a
   Status carries an is_closed flag distinct from done_ratio). The helper
   now takes status_is_closed and returns open AND past-due, dropping the
   done_ratio coupling.

Both are safe API changes — op-work-packages was a fresh stub with no
consumers yet. Tests updated (priority defaults None + with_priority;
is_overdue rewritten around open/closed status). 6 tests pass; fmt + this
crate's clippy clean on 1.95.
AdaWorldAPI pushed a commit that referenced this pull request Jul 1, 2026
Add the WorkPackage write path on top of the #64/#65 model, mirroring the
op-journals service house pattern.

- NewWorkPackage — the create write-model / input DTO (Deserialize, so an
  API layer can parse a request body). Required NOT NULL columns + optional
  priority/assignee/parent/description; to_work_package() maps it onto a
  fresh domain entity.
- WorkPackageStore — async persistence port (insert / get / list_for_project).
- WorkPackageService — create (validate subject non-blank -> build ->
  persist, returns the entity with its assigned id), find (NotFound),
  list_for_project.
- MemoryWorkPackageStore — in-memory impl for tests/prototyping.
- WorkPackageError + WorkPackageResult (thiserror).

6 service tests (persist+id, blank-subject rejection, optional-field
mapping, NotFound, project filter, write-model deserialize/default) + the 6
model tests = 12 pass. fmt clean; op-work-packages clippy-clean (0 warnings
in this crate; the transitive -D warnings op-core lints are pre-existing,
untouched).

Deps: async-trait + thiserror (runtime), tokio (dev, for #[tokio::test]).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants