Skip to content

feat: add work assignment doctype with member hour tracking (LAN-885)#294

Open
MarcCon wants to merge 48 commits into
version-15-hotfixfrom
lan-885
Open

feat: add work assignment doctype with member hour tracking (LAN-885)#294
MarcCon wants to merge 48 commits into
version-15-hotfixfrom
lan-885

Conversation

@MarcCon

@MarcCon MarcCon commented Jan 12, 2026

Copy link
Copy Markdown
Collaborator

This PR enables tracking of work hours for members through work assignments.

Features:

  • Work Assignment DocType
  • Work Hours Report
  • button to create work assignments directly from a member record

Open Questions:
Do we need a hourly account for a member? It is not necessary for the report.

@MarcCon MarcCon requested a review from barredterra January 12, 2026 08:45
MarcCon and others added 3 commits January 16, 2026 09:23
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>

@barredterra barredterra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Good start!

I think in the requirements I read that each Organization should be able to specify a number of working hours per year and we need to track the members´ actual working hours against that. And, ideally, also allow carrying over excess working hours to the next year.

Let's also add the Work Assignment to the connections in the LANDA Member form.

Comment thread landa/organization_management/doctype/work_assignment/work_assignment.js Outdated
Comment thread landa/organization_management/doctype/work_assignment/work_assignment.json Outdated
Comment thread landa/organization_management/report/work_hours/work_hours.py
@MarcCon

MarcCon commented Jan 26, 2026

Copy link
Copy Markdown
Collaborator Author

Good start!

I think in the requirements I read that each Organization should be able to specify a number of working hours per year and we need to track the members´ actual working hours against that. And, ideally, also allow carrying over excess working hours to the next year.

Let's also add the Work Assignment to the connections in the LANDA Member form.

My idea would be to add a DocType "Organization Work Hours" with a child table to show members of the organization with their hours from the Work Assignment DocType and validate them against the target hours.

@MarcCon MarcCon requested a review from barredterra January 26, 2026 09:39
@barredterra

Copy link
Copy Markdown
Member

See my alternative proposal in LAN-885

@barredterra barredterra assigned barredterra and unassigned MarcCon Feb 11, 2026
@barredterra

Copy link
Copy Markdown
Member

@MarcCon exactly, die Rechte haben gefehlt, sodass niemand buchen kann. Das müsste noch gefixt werden.

@MarcCon

MarcCon commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

Ah habs falsch verstanden, alles klar.

@MarcCon

MarcCon commented Apr 9, 2026

Copy link
Copy Markdown
Collaborator Author

Die offenen Issues sind erledigt, ich teste es noch als normaler Nutzer durch.

Edit:
Als Nutzer mit der Rolle "LANDA Local Organization Management" kann ich ein Work Assignment nur für den Verein des Nutzers erstellen. Ebenso sehe ich nur Work Assignments und Work Ledger Entries dieses Vereines.

In beiden Reports kann ich in den Filtern auch nur den eigenen Verein auswählen und ansehen.

@MarcCon MarcCon requested a review from barredterra April 9, 2026 17:58
@barredterra barredterra assigned barredterra and unassigned MarcCon Apr 21, 2026
Comment thread landa/organization_management/doctype/work_assignment/test_work_assignment.py Outdated
Comment thread landa/organization_management/report/work_hours/work_hours.json Outdated
@barredterra barredterra assigned MarcCon and unassigned barredterra Apr 27, 2026
@MarcCon

MarcCon commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile

@greptile-apps

greptile-apps Bot commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Confidence Score: 4/5

Mostly safe to merge; create_yearly_negative_entries has no guard against being run more than once in the same year, which would double every member's annual obligation.

The core submit/cancel ledger logic is correct and the previously flagged delete bug is properly fixed. The one remaining concern is that create_yearly_negative_entries is not idempotent — a second execution (scheduler restart, manual console call, test run) silently creates duplicate obligation entries for every member with no way to detect them automatically.

landa/organization_management/doctype/work_ledger_entry/work_ledger_entry.py — the yearly scheduler function needs a duplicate-run guard.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User
    participant WorkAssignment
    participant WorkLedgerEntry
    participant Scheduler
    participant Organization

    User->>WorkAssignment: Submit
    WorkAssignment->>WorkLedgerEntry: insert() per member row
    Note over WorkLedgerEntry: hours_change = member.duration

    User->>WorkAssignment: Cancel
    WorkAssignment->>WorkLedgerEntry: delete_doc() per linked entry

    Scheduler->>WorkLedgerEntry: create_yearly_negative_entries() [Jan 1]
    WorkLedgerEntry->>Organization: "get_list (expected_work_hours_per_year > 0)"
    WorkLedgerEntry->>WorkLedgerEntry: insert(-expected_hours) per member

    User->>Organization: Save (change expected_work_hours_per_year)
    Organization->>WorkLedgerEntry: create_expected_hours_adjustment_entries(old - new)
    WorkLedgerEntry->>WorkLedgerEntry: insert(adjustment) per member
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User
    participant WorkAssignment
    participant WorkLedgerEntry
    participant Scheduler
    participant Organization

    User->>WorkAssignment: Submit
    WorkAssignment->>WorkLedgerEntry: insert() per member row
    Note over WorkLedgerEntry: hours_change = member.duration

    User->>WorkAssignment: Cancel
    WorkAssignment->>WorkLedgerEntry: delete_doc() per linked entry

    Scheduler->>WorkLedgerEntry: create_yearly_negative_entries() [Jan 1]
    WorkLedgerEntry->>Organization: "get_list (expected_work_hours_per_year > 0)"
    WorkLedgerEntry->>WorkLedgerEntry: insert(-expected_hours) per member

    User->>Organization: Save (change expected_work_hours_per_year)
    Organization->>WorkLedgerEntry: create_expected_hours_adjustment_entries(old - new)
    WorkLedgerEntry->>WorkLedgerEntry: insert(adjustment) per member
Loading

Fix All in Cursor

Reviews (3): Last reviewed commit: "Merge branch 'version-15-hotfix' into la..." | Re-trigger Greptile

Comment thread landa/organization_management/doctype/work_assignment/work_assignment.py Outdated
@MarcCon

MarcCon commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator Author

@greptile

Comment on lines +29 to +55
def create_yearly_negative_entries():
"""Create negative Work Ledger Entries for all members based on expected work hours per year."""

organizations = frappe.get_list(
"Organization",
filters={"expected_work_hours_per_year": [">", 0]},
fields=["name", "expected_work_hours_per_year"],
)

if not organizations:
return

# Get all active members for each organization
for org in organizations:
members = frappe.get_list(
"LANDA Member",
filters={"organization": org.name},
fields=["name"],
)

for member in members:
ledger_entry = frappe.new_doc("Work Ledger Entry")
ledger_entry.member = member.name
ledger_entry.organization = org.name
ledger_entry.date = f"{now_datetime().year}-01-01"
ledger_entry.hours_change = -org.expected_work_hours_per_year
ledger_entry.insert()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 No idempotency guard in create_yearly_negative_entries

If this function runs more than once for the same year — scheduler restart, a manual frappe bench execute call during maintenance, or a test run — every member in every qualifying organisation gets duplicate negative entries. Because there is no check for an existing entry dated {year}-01-01 without a work_assignment, the second run silently doubles every member's annual obligation. The corruption is permanent and would require manual clean-up per member.

A safe guard would be to skip creating entries for any member that already has a no-work_assignment entry on {year}-01-01 for the current run year.

Fix in Cursor

@MarcCon MarcCon requested a review from barredterra June 25, 2026 09:37
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.

3 participants