Commit Graph

1291 Commits

Author SHA1 Message Date
Soham Kulkarni
3eaea74a51 Merge pull request #56656 from sokumon/revamp-workspaces
chore: exporting workspaces with sidebars
2026-07-02 05:39:24 +05:30
Mihir Kandoi
b2ad93be81 perf: use recursive CTEs for BOM and Task tree traversal
Reimplements get_ancestor_boms, BOM.traverse_tree, Task.check_recursion and the BOM Explorer report as single recursive-CTE queries (frappe.qb recursive=True), replacing query-per-node walks (N->1). Task cycle detection now catches cycles at any depth (was capped at 15 nodes). Requires the framework recursive-CTE support (frappe#40464).
2026-07-01 22:10:57 +05:30
sokumon
3b25878d71 fix: remove text from projects workspace 2026-07-01 13:29:25 +05:30
sokumon
5a32866b93 chore: export more workspaces 2026-06-30 17:00:54 +05:30
Mihir Kandoi
0776f7f7fa test: convert trivially-equivalent raw SQL to ORM helpers
Convert test-only raw frappe.db.sql calls that have an exact ORM
equivalent: full-table/filtered deletes -> frappe.db.delete, count ->
frappe.db.count, row-existence assertions -> frappe.db.exists,
single-row scalar fetches -> frappe.db.get_value, and simple
equality/range-filter selects -> frappe.get_all. No behaviour change.

Raw SQL that genuinely needs it is left as-is (dynamic identifiers,
aggregates/group-by, positional as_list consumers, DB-catalog
introspection).
2026-06-26 10:44:17 +05:30
Mihir Kandoi
d2ff0913df Merge pull request #56476 from mihir-kandoi/messages/projects
chore: rewrite user-facing messages in projects module
2026-06-25 18:35:00 +05:30
Mihir Kandoi
8de7a25d16 Merge pull request #56496 from mihir-kandoi/messages-js/projects
chore: rewrite user-facing JS messages in projects module
2026-06-25 17:48:08 +05:30
Nabin Hait
555ce3fc2a Merge pull request #56322 from nabinhait/test-project-percent-complete
test(projects): cover untested project and project-template functions
2026-06-25 17:44:19 +05:30
Nabin Hait
57cb133aaf Merge pull request #56325 from nabinhait/test-task-timesheet-coverage
test(projects): cover untested task, timesheet, and activity-cost functions
2026-06-25 17:43:16 +05:30
Mihir Kandoi
64fef7e108 chore: rewrite user-facing JS messages in Projects module
Conservative cleanup of frappe.throw/msgprint messages per the message style
guide; meaning, severity, and .format() arguments are unchanged:

- index bare {} placeholders as {0}/{1}/... so translators can reorder
- move f-strings / .format() / concatenation out of _() (they break gettext
  extraction and never translate)
- wrap translatable dynamic values (DocType/Select labels) in _()
- fix grammar and colloquialisms
- drop no-op _() wrapping runtime-built strings

Part of #53976.
2026-06-25 17:38:20 +05:30
Mihir Kandoi
4efb43d977 chore: rewrite user-facing messages in Projects module
Conservative cleanup of frappe.throw/msgprint messages per the message style
guide; meaning, severity, and .format() arguments are unchanged:

- index bare {} placeholders as {0}/{1}/... so translators can reorder
- move f-strings / .format() / concatenation out of _() (they break gettext
  extraction and never translate)
- wrap translatable dynamic values (DocType/Select labels) in _()
- fix grammar and colloquialisms
- drop no-op _() wrapping runtime-built strings

Part of #53976.
2026-06-25 17:37:08 +05:30
Mihir Kandoi
3be80d8e87 fix: include list in get_events filters type hint
The calendar view sends filters as a list of [doctype, field, op, value]
conditions (filter_area.get()); get_filter_conditions_qb accepts dict or list.
2026-06-24 22:01:55 +05:30
Mihir Kandoi
cb236dedfc refactor: parse native JSON request args in projects/doctype/timesheet/timesheet.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:33 +05:30
Mihir Kandoi
5629f81809 refactor: parse native JSON request args in projects/doctype/task/task.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:33 +05:30
Mihir Kandoi
e432f8284b refactor: parse native JSON request args in projects/doctype/project/project.py
Use frappe.parse_json instead of json.loads so the whitelisted endpoints
accept native JSON types (list/dict/bool) in addition to JSON strings.
2026-06-24 20:37:33 +05:30
Mihir Kandoi
c79febf403 fix(projects): drop redundant distinct from portal project list (Postgres ordering) (#56362)
get_project_list builds a single-table query (no joins) with fields="*",
which always selects the unique PK `name`, so distinct=True can never
deduplicate any rows. It is a no-op on the result set for both engines.

It is not a no-op on ordering, though: frappe.db drops the ORDER BY clause
for distinct queries on Postgres (Postgres requires every ORDER BY term to
appear in the select list under DISTINCT), so the website project list came
back unordered on Postgres while MariaDB returned it ordered by `order_by`.

Removing the redundant flag leaves the MariaDB result and order untouched
and restores the same ordering on Postgres.
2026-06-23 11:47:38 +00:00
nishkagosalia
359717115f fix: company default handling in purchase transactions made from project 2026-06-23 12:33:31 +05:30
Nabin Hait
0602a22e4b test(project): cover costing and billing roll-ups
Covers the sales/billing roll-up (total_sales_amount, total_billed_amount,
gross margin) via the whitelisted update_costing_and_billing, and
consumed-material cost from a project-linked Stock Entry issue. The
purchase-cost roll-up is already covered by the Purchase Invoice tests.
2026-06-22 18:38:51 +05:30
Nabin Hait
b2bae839ac test(activity-cost): cover default-cost title and duplication
Covers the no-employee path (title set to the activity type and the
default-cost duplication guard) and employee_name being fetched for the
title. Brings activity_cost.py to full coverage.
2026-06-22 18:27:00 +05:30
Nabin Hait
6ef8b41c3c test(project-template): cover dependency-task validation
A template task that depends on another task requires that dependency to
also be present in the template's task list; covers both the rejection
and the valid case.
2026-06-22 18:25:17 +05:30
Nabin Hait
bab97aaad0 test(timesheet): cover activity cost and billing-rate helpers
Covers get_activity_cost falling back to the Activity Type rates (and the
empty result for an unknown type), plus get_timesheet_data and
get_timesheet_detail_rate for a billable timesheet detail.
2026-06-22 18:14:40 +05:30
Nabin Hait
14b83b46ac test(task): cover bulk actions, template deps, and delete guards
Covers the whitelisted set_multiple_status and add_multiple_tasks helpers
(including the blank-subject skip), the template-task dependency
validation, the on_trash child-exists guard, and a child task
registering itself in its parent's depends_on.
2026-06-22 18:14:38 +05:30
Nabin Hait
53f0049e75 test(project): cover create_duplicate_project and set_project_status
Both are whitelisted, UI-triggered functions that had no server tests.
Covers duplicating a project with its tasks (and the same-name guard),
and bulk-setting a project plus its tasks to a terminal status (and the
invalid-status guard).
2026-06-22 18:04:53 +05:30
Nabin Hait
76b0123778 test(project): cover update_percent_complete for all methods
Adds assertions for the four percent_complete_method paths (Manual is
already covered), plus the status transitions: 100% flips a project to
Completed, reopening a task flips it back to Open, and a Cancelled
project keeps its status. The method was previously unasserted.
2026-06-22 17:38:23 +05:30
Mihir Kandoi
851e70b0f3 Merge pull request #56233 from mihir-kandoi/pg-timesheet-coalesce
fix(projects): use Coalesce for timesheet portal sales_invoice (not bitwise OR)
2026-06-21 10:51:25 +05:30
Mihir Kandoi
5a0e7f57a3 fix(projects): use Coalesce for timesheet portal sales_invoice (not bitwise OR)
get_timesheets_list selected `timesheet.sales_invoice | detail.sales_invoice`,
intending COALESCE (pick the parent timesheet's invoice, else the detail's) -- the
original raw SQL was COALESCE(ts.sales_invoice, tsd.sales_invoice). pypika's `|` is a
bitwise OR, not a coalesce:

- Postgres: `varchar | varchar` -> "operator does not exist" (hard error).
- MariaDB: bitwise OR coerces the operands to integers; with a NULL detail invoice the
  result is NULL, so the portal showed no invoice even when the timesheet was billed.

Replace with Coalesce(table.sales_invoice, child_table.sales_invoice).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:27:14 +05:30
Mihir Kandoi
a483177690 fix(projects): make Project timeline GROUP BY Postgres-valid
get_timeline_data grouped Timesheet Detail by Date(from_time) but selected
UnixTimestamp(from_time) (the full timestamp, ungrouped). MariaDB
arbitrary-picks a row's timestamp; Postgres rejects it ("must appear in the
GROUP BY clause"), so the Project timeline (calendar heatmap) is broken on PG.

Select UnixTimestamp(Date(from_time)) — the day's epoch — which is the
timeline key and matches the GROUP BY. CurDate() - Interval(years=1) is
portable and kept as-is.

Adds a test (no coverage existed) that records a timesheet against a project
and asserts get_timeline_data returns day-bucketed counts, on both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 09:44:48 +05:30
Mihir Kandoi
7793e31e4e fix(projects): repair project_update daily_reminder and convert to ORM
daily_reminder/email_sending used raw frappe.db.sql with two portability
and correctness problems:

- The update query selected `progress` and `progress_details` from
  `tabProject Update`, but those columns do not exist on the Project
  Update doctype, so the query raised on BOTH MariaDB and Postgres
  (the function is whitelisted-only, so the bug was latent). Drop the
  non-existent columns and the corresponding "Project Status"/"Notes"
  cells from the summary table.
- `DATE_ADD(CURRENT_DATE, INTERVAL -1 DAY)` (MySQL-only) and a
  `CURRENT_DATE` Holiday lookup are not valid on Postgres.

Convert to ORM: frappe.get_all for Project/Project Update/Project User,
frappe.db.count for drafts, frappe.db.exists for the holiday check, and
add_days(today(), -1) for the date filter. Also str() the frequency in the
message so a NULL/empty frequency (Postgres returns None) does not raise.

Adds a test (the file was an empty stub) that creates a project + an update
dated yesterday and asserts the reminder finds it and runs end to end on
both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 05:33:47 +05:30
Mihir Kandoi
9dcd561778 test(projects): fix time-of-day flaky daily-timesheet-summary test
make_timesheet(simulate=True) logs at now_datetime(); when the suite runs late
in the day under the site timezone the 2h log crosses midnight, so its to_time
falls outside the report's `to_time <= end-of-day` bound and the submitted
timesheet is (correctly) excluded — making test_submitted_timesheet_in_summary
fail in an evening window (observed at 22:51 IST in CI). Pin the log to a fixed
mid-day window on today so the assertion is time-of-day independent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 23:29:45 +05:30
Nabin Hait
61927b61fe Merge pull request #56139 from nabinhait/refactor-si-timesheet-billing
refactor(sales_invoice): simplify TimesheetBillingService link decision
2026-06-19 16:47:16 +05:30
Mihir Kandoi
bbc684aa80 Merge pull request #56142 from mihir-kandoi/pg-projects
refactor(postgres): port Projects module queries to the query builder
2026-06-19 15:54:52 +05:30
Nabin Hait
dd7891e18f test(timesheet): characterize sales-invoice link/unlink and submit guard
Pin TimesheetBillingService behaviour before refactor: billing a timesheet
into a Sales Invoice links the timesheet detail and marks it Billed on submit,
and clears the link / reverts status on cancel; an unsubmitted timesheet
cannot be invoiced.
2026-06-19 15:31:31 +05:30
Mihir Kandoi
ea665d1a9b refactor(postgres): port Projects module queries to the query builder
Convert raw `frappe.db.sql` across the Projects module to `frappe.qb` / the ORM
so the same code runs on MariaDB and Postgres. Behaviour is preserved on
MariaDB; the conversions also make these paths valid under Postgres' stricter
SQL (GROUP BY, case-sensitivity, empty-string handling).

Conversions of note (behaviour kept identical to the MariaDB original):
- project.get_users_for_project: search selects the stored full_name instead of
  concat_ws(first, middle, last) (concat_ws diverges on Postgres, where empty
  Data fields are NULL) and wraps Locate in LOWER() to keep MariaDB's
  case-insensitive result ordering.
- project costing: percent-complete and sales/billed-amount aggregates rebuilt
  as Sum() query-builder selects.
- task.reschedule_dependent_tasks: the correlated subquery is split into a
  `Task Depends On` parent-pluck + a Task lookup (same rows, no nested SQL).
- timesheet.get_events: user-permission match conditions move to the query-builder
  form via get_event_conditions_qb; calendar columns rebuilt with Concat/Round.
- report/project_wise_stock_tracking & report/daily_timesheet_summary: GROUP BY
  cost aggregates and the timesheet date window (timestamp(to_date,'24:00:00') ->
  end-of-day via get_combine_datetime) rebuilt to satisfy Postgres.
- search helpers (query_task, get_project, get_timesheet) use frappe.qb.get_query
  with ignore_permissions=False in place of build_match_conditions/get_match_cond.

Tests (run on both MariaDB and Postgres, --lightmode):
- Existing project/task/timesheet/activity_cost suites kept green (27 tests).
- New project_wise_stock_tracking test drives all three cost aggregates with
  positive data (purchased / issued / delivered GROUP BY) plus get_project_details.
- New daily_timesheet_summary test covers the date-window join.

Not included: project_update.py is deferred. Its daily_reminder()/email_sending()
select `progress`/`progress_details`, columns that do not exist on the Project
Update doctype, so the function errors when invoked regardless of backend - a
pre-existing bug that needs an email-rework, not just a query port.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:22:20 +05:30
Nabin Hait
db2e2105ab fix: Use get_value instead of get_doc
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
2026-06-19 14:37:32 +05:30
Nabin Hait
900c71840c test(projects): set company on second project to fix CI mandatory error
The second Project in test_sales_order_link_is_not_overwritten_by_second_project
was inserted without a company, which only succeeds when a default company is
configured. On a fresh CI site this raised MandatoryError. Set company from the
sales order explicitly.
2026-06-19 12:37:43 +05:30
Mihir Kandoi
8dacf62da0 Revert "refactor(manufacturing, projects): make raw SQL portable to PostgreSQ…"
This reverts commit 2d24eedab2.
2026-06-16 23:29:28 +05:30
Mihir Kandoi
2d24eedab2 refactor(manufacturing, projects): make raw SQL portable to PostgreSQL (parity rollout 2/9) (#56008)
refactor(manufacturing, projects): make raw SQL portable to PostgreSQL

Convert the MariaDB-only raw `frappe.db.sql` in the Manufacturing and Projects
modules to the cross-database query builder / ORM, and fix the non-portable
constructs that remain. Every change is a no-op on MariaDB (identical rendered
SQL / identical results) and only brings PostgreSQL — standards-strict where
MySQL is lax — in line.

Areas: BOM (cost/where-used/explosion), Work Order (operations, required items,
mapper, stock report), Workstation, Production Plan sub-assembly/explosion
queries, BOM Stock Analysis / Process Loss / Work Order Stock reports; Projects
(project, task, timesheet, activity cost, project update), Daily Timesheet
Summary and Project-wise Stock Tracking reports.

Part of the staged MariaDB<->PostgreSQL parity rollout (module 2 of 9).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 16:20:36 +00:00
Nabin Hait
b2eb6a69c1 fix(projects): don't overwrite existing Sales Order project link
Creating a Project with a `sales_order` set used `frappe.db.set_value`
to unconditionally write the Sales Order's `project` field. When several
projects were created for the same Sales Order, each new project silently
overwrote the previous link, leaving earlier projects orphaned.

Only back-link the Sales Order when it is not already tied to another
project, and write the value through the document's `db_set` so the
modified timestamp and realtime update are handled. A warning is shown
when an existing link is left untouched.

Fixes #52179
2026-06-14 20:17:08 +05:30
sokumon
90aba582ec chore: export workspaces with new schema 2026-06-12 13:08:48 +05:30
Ankush Menat
ba936eefab fix: Add authorization checks on internal functions (#55709) 2026-06-08 14:49:32 +05:30
Nabin Hait
04443ae29e Merge upstream/develop into erpnext-refactoring
Resolve 4 conflicts from Phase 7 service/mapper extraction vs upstream:
- asset.py: take extraction; repoint dangling make_asset_movement JS to mapper
- job_card: port upstream field_no_map(naming_series) into mapper.make_subcontracting_po
- sales_order: port upstream rows-index fix into mapper.make_delivery_note
- sales_invoice (Phase 7): take service delegations; port upstream SQL->QB/ORM
  changes for get_warehouse, get_all_mode_of_payments, get_discounting_status,
  clear_unallocated_mode_of_payments, and set_pos_fields(POS DN skip) into services
2026-06-03 11:05:01 +05:30
Mihir Kandoi
78f9434d14 refactor: resolve regression-safe CodeQL code-quality findings (#55531)
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 08:25:32 +05:30
Nabin Hait
530e587bf2 refactor: use mapper paths directly, drop re-export shims
Repoint all JS method strings and Python imports for mapper functions
across 18 doctypes from the doctype module to its mapper module, and
remove the now-unused re-export shims from each doctype file (keeping
only names used internally).
2026-06-01 18:17:56 +05:30
nareshkannasln
9d8f3863f2 fix(project): update customer and sales order as no copy 2026-05-22 16:47:46 +05:30
Rohit Waghchaure
4d14727b26 fix: linter issue 2026-05-20 23:31:09 +05:30
Rohit Waghchaure
e4b5e6bd1e refactor: split stock_entry.py into multiple files for better readability 2026-05-19 18:41:31 +05:30
Jaypal Lakum
3532c1cc69 fix(task): update depends_on for closing date and review date #54850 (#54852) 2026-05-12 09:53:42 +00:00
Hemil-Sangani
231dd1856f fix(project): use user.email for invitations and skip disabled users. (#54561)
* fix(project): use user.email for invitations and skip disabled users.

* Update erpnext/projects/doctype/project/project.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* fix(project): remove duplicate loop causing indentation error

* fix(project): resolve pre-commit hook failure

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2026-04-30 07:53:32 +00:00
ruthra kumar
b16dd3f2dd refactor(test): remove explicit sql delete calls 2026-04-22 10:33:25 +05:30
Nihantra Patel
a2ec597e3d fix: set letter_head_for for letterhead 2026-04-20 16:58:35 +05:30