Commit Graph

989 Commits

Author SHA1 Message Date
Diptanil Saha
9b4c8a8d7f fix(crm): using get_list instead of get_all in get_opportunities (#56463) 2026-06-25 10:00:44 +00:00
Mihir Kandoi
6a4afd1733 fix: parse native JSON contacts args in CRM prospect/customer endpoints
create_prospect_against_crm_deal and create_customer read `contacts`
from form_dict (json.loads(doc.contacts) / customer_data.get("contacts")),
which arrives as a native list under JSON body mode. Use frappe.parse_json.
2026-06-24 20:55:01 +05:30
Mihir Kandoi
ffa85a4ed6 refactor: parse native JSON request args in crm/frappe_crm_api.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:32 +05:30
Mihir Kandoi
50f2654eb1 refactor: parse native JSON request args in crm/doctype/opportunity/opportunity.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:32 +05:30
Mihir Kandoi
dcc8d08521 refactor: parse native JSON request args in crm/doctype/contract_template/contract_template.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:32 +05:30
Diptanil Saha
0ab812c3ec feat(crm_settings): enable frappe crm data synchronization (#56268) 2026-06-23 21:14:03 +05:30
Mihir Kandoi
b4aae9dea1 fix(crm): render Lead Details address consistently across engines
The Lead Details report concatenated address_line1 and address_line2 with
CONCAT_WS. An unfilled optional Data field is stored as '' on MariaDB but as
NULL on PostgreSQL; CONCAT_WS keeps the empty string (leaving a trailing
", ") on MariaDB while Postgres drops the NULL, so the same lead rendered a
different address on each engine.

Wrap both parts in NULLIF(part, '') so empty values are treated as NULL on
both engines: the report now produces the same clean address (no trailing
separator) everywhere.
2026-06-23 08:58:30 +05:30
Nabin Hait
43d2c7335d refactor(lead): name the loops in remove_link_from_prospect
The outer and inner loops both used 'd'; name them linked_prospect and lead
so the prospect/lead iteration reads clearly. No behaviour change.
2026-06-22 15:59:26 +05:30
Nabin Hait
8f69697212 fix(lead): don't crash deriving lead name when only ignore_mandatory is set
set_lead_name fell through to email_id.split('@') when a lead had no name,
company or email but ignore_mandatory was set (e.g. data import), raising
AttributeError on a None email. Only derive from email when one exists; the
lead name is then left blank, as intended for that path.
2026-06-22 15:59:25 +05:30
Nabin Hait
b4c9827318 Merge pull request #56295 from nabinhait/test-lead-coverage
test(lead): cover lead details and prospect sync/unlink
2026-06-22 14:05:35 +05:30
Nabin Hait
95b82eeba8 Merge pull request #56290 from nabinhait/test-opportunity-lost-flow
test(opportunity): improve coverage (lost flow, auto-close, item details, prospect sync)
2026-06-22 14:05:06 +05:30
Nabin Hait
19ba681e16 Merge pull request #56296 from nabinhait/opportunity-auto-close-default-days
fix(crm): drive opportunity auto-close days from CRM Settings, not a hardcoded fallback
2026-06-22 14:04:00 +05:30
Nabin Hait
f1b6a7d690 fix(crm): drive opportunity auto-close days from CRM Settings, not a hardcoded fallback
auto_close_opportunity fell back to 15 days in code when the CRM Setting was
blank (and its docstring still said 7). The field already defaults to 15, so
read the value straight from CRM Settings and add a patch to backfill 15 for
existing sites that left it blank, keeping the same auto-close schedule.
2026-06-22 13:05:50 +05:30
Nabin Hait
4c6b030a4b test(lead): cover lead details fetch and prospect sync/unlink
Add tests for get_lead_details and the Lead <-> Prospect lifecycle: editing a
lead syncs into its Prospect Lead row, and deleting the only lead of a
prospect removes the prospect. Lead controller coverage 65% -> 74%.
2026-06-22 12:50:25 +05:30
Nabin Hait
017e09eaac test(opportunity): cover item details, auto-close and prospect sync
Add tests for get_item_details, auto_close_opportunity (a stale Replied
opportunity is closed, a recent one is not) and the Opportunity -> Prospect
opportunity sync. Opportunity controller coverage 62% -> 80%.
2026-06-22 12:45:12 +05:30
Nabin Hait
61c2e7ad6e test(opportunity): cover the mark-as-lost flow
declare_enquiry_lost had almost no coverage. Add tests that marking an
Opportunity as lost records the lost reasons, competitors and detailed
reason and sets status to Lost, and that it is blocked when an active
(submitted) Quotation exists.
2026-06-22 12:33:19 +05:30
Diptanil Saha
c188ed59ec fix(lead): added missing read permission check on get_lead_details (#56272) 2026-06-21 21:46:53 +00:00
Mihir Kandoi
413ec60a3e refactor(postgres): address Greptile review on prospects report
- Fix N+1: the per-lead conversion issued 4 queries per lead (Opportunity, Quotation,
  Issue, Communication). Collect the reference documents for all leads in 3 bulk
  queries, then one Communication query per lead -> ~N+3 round-trips instead of 4N,
  matching the original single-query-per-lead cost.
- Constrain reference_doctype in the Communication lookup (names are unique only within
  a doctype), closing a latent cross-doctype name-collision gap the original also had.

Both-engine test still green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:26:45 +05:30
Mihir Kandoi
5104007d12 refactor(postgres): port CRM module queries to the query builder
Convert the remaining raw frappe.db.sql in the CRM module to frappe.qb / ORM so the
queries run on PostgreSQL as well as MariaDB. Faithful conversions -- no MariaDB
behaviour change:

- opportunity.py, doctype/utils.py (get_last_interaction)
- reports: campaign_efficiency, first_response_time_for_opportunity (GROUP BY on the
  grouped Date(creation) + Avg -- Postgres-valid), lead_conversion_time,
  prospects_engaged_but_not_converted

lead_conversion_time also keeps the IS NOT NULL communication-date guard (forward-port
of the fix already on develop). Also drops invalid backtick notation from two get_all
order_by clauses in doctype/utils.py (order_by="`creation` DESC"), which frappe's
query engine rejects -- a latent failure on both engines, surfaced by the new test.

Tests: existing opportunity suite plus new both-engine tests for the four previously
untested reports/utils. All green on MariaDB and PostgreSQL.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:15:20 +05:30
Mihir Kandoi
1f4702bde7 fix(crm): skip rows with no first-contact date in lead conversion time
Address review (#56105): when there's no matching communication, first_contact is
None and date_diff(invoice_date, None) treats None as today, giving a wrong
(negative) duration. Skip the entry instead, mirroring the communication_count guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 21:04:33 +05:30
Mihir Kandoi
5787951ed1 fix(crm): guard first_contact result before indexing (lead conversion time)
Address review (#56105): the IS NOT NULL guard can return no rows (the count above
filters on sender, this query on recipients), so [0][0] would raise IndexError.
Fall back to None when empty, matching the prior behaviour for that case.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 20:50:31 +05:30
Mihir Kandoi
cfa6d286ad fix(postgres): other-class parity fixes (case-folding & null-ordering) for merged queries
Parity sweep findings in queries already merged in develop:

- pos_invoice.py: serial_no return-match matched case-sensitively on Postgres (case-insensitive
  on MariaDB). Replaced the get_all or_filters lookup with a qb query that case-folds both sides
  via Lower() (no-op on MariaDB).
- controllers/queries.py + pick_list.py: the Locate()-based relevance ranking in link-query
  ORDER BY is case-sensitive on Postgres (Strpos) vs case-insensitive on MariaDB (Locate), so
  autocomplete order differed. Lower() both arguments so the ranking matches on both engines.
- crm/lead_conversion_time.py: "first contact" used ORDER BY communication_date LIMIT 1 read by
  index; a NULL date sorts first on MariaDB but last on Postgres, changing the result. Added
  `communication_date IS NOT NULL` so both engines return the earliest real contact date.

Verified on MariaDB and Postgres: test_pick_list 40/40, test_pos_invoice 26/26 on both engines.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 20:36:30 +05:30
Mihir Kandoi
0b795a628f fix(postgres): use portable GroupConcat in Lost Opportunity report
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 15:19:16 +05:30
Mihir Kandoi
8a566e6ba5 fix(postgres): satisfy strict GROUP BY in Sales Pipeline Analytics report
Wrap the non-aggregated, functionally-dependent column(s) in Max()/Min() (or add
them to GROUP BY) so the report's grouped query is valid under PostgreSQL's strict
GROUP BY. No behaviour change on MariaDB.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 13:19:00 +05:30
Raffael Meyer
93d6be2ed7 fix(Lead): stop storing Gravatar image URLs for Leads (#55880) 2026-06-13 19:03:29 +02:00
rohitwaghchaure
bd60a9be90 Merge pull request #55849 from rohitwaghchaure/fixed-permissions-for-whitelist-functions
fix: permission for whitelist functions
2026-06-13 18:36:46 +05:30
Rohit Waghchaure
a64466561f fix: permission for whitelist functions 2026-06-13 17:45:37 +05:30
Diptanil Saha
c933e34914 fix: opportunity creation from contact us page (#55841) 2026-06-13 04:47:45 +00:00
ruthra kumar
d6ba0f0eca Merge pull request #55486 from Shllokkk/crm-create-customer-fix
Validations in CRM-api endpoints
2026-06-08 15:10:26 +05:30
Ankush Menat
ba936eefab fix: Add authorization checks on internal functions (#55709) 2026-06-08 14:49:32 +05:30
Nabin Hait
9b1157c914 fix: set options Email for customer_email field in appointment 2026-06-06 20:41:16 +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
Shllokkk
e460e83516 fix: use new_doc with field allowlist in CRM integration endpoints 2026-05-31 18:42:26 +05:30
Nabin Hait
28c3d24b86 refactor(lead): move mapping functions to mapper.py 2026-05-29 13:26:31 +05:30
Nabin Hait
9b85773757 refactor(opportunity): move mapping functions to mapper.py 2026-05-29 13:23:56 +05:30
Raffael Meyer
992800f3dd fix: implement get_notification_email hook on Opportunity, Prospect and Customer (#54789) 2026-05-08 22:32:38 +02:00
Sakthivel Murugan S
a4a389bd41 fix(crm): handle empty _assign in appointment auto assignment (#54782) 2026-05-08 17:51:08 +05:30
Raffael Meyer
6f9f3d0a5c feat(Lead)!: send notifications to lead owner (#53959) 2026-05-08 12:40:23 +02: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
diptanilsaha
d15cd08e72 refactor(lost_opportunity_report): replaced raw_sql with query builder (#54136) 2026-04-08 23:38:43 +05:30
ruthra kumar
d6755c3d14 Merge pull request #53343 from Shllokkk/email-campaign-fix
fix(email_campaign): prevent unsubscribing entire campaign when email group member unsubscribes
2026-03-27 10:26:49 +05:30
diptanilsaha
e136bfbb61 fix(contract_template): restrict create, write and delete access only to System Manager (#53787) 2026-03-25 14:43:45 +00:00
ruthra kumar
ed76d6699a refactor(test): move commits inside test guard clause 2026-03-24 11:57:02 +05:30
ruthra kumar
21b25ce96c refactor(test): hardcoded names over dynamic 2026-03-18 20:59:38 +05:30
ruthra kumar
5237e58f29 refactor(test): remove explicit call to master data setup 2026-03-18 20:59:38 +05:30
ruthra kumar
e67165d6ce refactor(test): hardcoded names over dynamic ones
Much faster bootstrap without those get_doc calls
2026-03-18 20:59:38 +05:30
ruthra kumar
cb693e05bf refactor(test): tax rule; removed setUpClass, tearDownClass 2026-03-18 20:59:38 +05:30
ruthra kumar
4777b060ba refactor(test): make opportunity summary by sales stage deterministic 2026-03-18 20:59:38 +05:30
ruthra kumar
3c904cbc5f refactor(test): make leads deterministic 2026-03-18 20:59:37 +05:30