Merge branch 'develop' into exit-interview
This commit is contained in:
commit
4686527928
@ -374,12 +374,13 @@ def make_gl_entries(doc, credit_account, debit_account, against,
|
||||
frappe.db.commit()
|
||||
except Exception as e:
|
||||
if frappe.flags.in_test:
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
|
||||
raise e
|
||||
else:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
|
||||
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
|
||||
frappe.flags.deferred_accounting_error = True
|
||||
|
||||
def send_mail(deferred_process):
|
||||
@ -446,10 +447,12 @@ def book_revenue_via_journal_entry(doc, credit_account, debit_account, against,
|
||||
|
||||
if submit:
|
||||
journal_entry.submit()
|
||||
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
traceback = frappe.get_traceback()
|
||||
frappe.log_error(message=traceback)
|
||||
frappe.log_error(title=_('Error while processing deferred accounting for Invoice {0}').format(doc.name), message=traceback)
|
||||
|
||||
frappe.flags.deferred_accounting_error = True
|
||||
|
||||
|
@ -57,8 +57,10 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
je.finance_book = d.finance_book
|
||||
je.remark = "Depreciation Entry against {0} worth {1}".format(asset_name, d.depreciation_amount)
|
||||
|
||||
credit_account, debit_account = get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account)
|
||||
|
||||
credit_entry = {
|
||||
"account": accumulated_depreciation_account,
|
||||
"account": credit_account,
|
||||
"credit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name,
|
||||
@ -66,7 +68,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
}
|
||||
|
||||
debit_entry = {
|
||||
"account": depreciation_expense_account,
|
||||
"account": debit_account,
|
||||
"debit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name,
|
||||
@ -132,6 +134,20 @@ def get_depreciation_accounts(asset):
|
||||
|
||||
return fixed_asset_account, accumulated_depreciation_account, depreciation_expense_account
|
||||
|
||||
def get_credit_and_debit_accounts(accumulated_depreciation_account, depreciation_expense_account):
|
||||
root_type = frappe.get_value("Account", depreciation_expense_account, "root_type")
|
||||
|
||||
if root_type == "Expense":
|
||||
credit_account = accumulated_depreciation_account
|
||||
debit_account = depreciation_expense_account
|
||||
elif root_type == "Income":
|
||||
credit_account = depreciation_expense_account
|
||||
debit_account = accumulated_depreciation_account
|
||||
else:
|
||||
frappe.throw(_("Depreciation Expense Account should be an Income or Expense Account."))
|
||||
|
||||
return credit_account, debit_account
|
||||
|
||||
@frappe.whitelist()
|
||||
def scrap_asset(asset_name):
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
|
@ -868,6 +868,72 @@ class TestDepreciationBasics(AssetSetup):
|
||||
self.assertFalse(asset.schedules[1].journal_entry)
|
||||
self.assertFalse(asset.schedules[2].journal_entry)
|
||||
|
||||
def test_depr_entry_posting_when_depr_expense_account_is_an_expense_account(self):
|
||||
"""Tests if the Depreciation Expense Account gets debited and the Accumulated Depreciation Account gets credited when the former's an Expense Account."""
|
||||
|
||||
asset = create_asset(
|
||||
item_code = "Macbook Pro",
|
||||
calculate_depreciation = 1,
|
||||
available_for_use_date = "2019-12-31",
|
||||
depreciation_start_date = "2020-12-31",
|
||||
frequency_of_depreciation = 12,
|
||||
total_number_of_depreciations = 3,
|
||||
expected_value_after_useful_life = 10000,
|
||||
submit = 1
|
||||
)
|
||||
|
||||
post_depreciation_entries(date="2021-06-01")
|
||||
asset.load_from_db()
|
||||
|
||||
je = frappe.get_doc("Journal Entry", asset.schedules[0].journal_entry)
|
||||
accounting_entries = [{"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts]
|
||||
|
||||
for entry in accounting_entries:
|
||||
if entry["account"] == "_Test Depreciations - _TC":
|
||||
self.assertTrue(entry["debit"])
|
||||
self.assertFalse(entry["credit"])
|
||||
else:
|
||||
self.assertTrue(entry["credit"])
|
||||
self.assertFalse(entry["debit"])
|
||||
|
||||
def test_depr_entry_posting_when_depr_expense_account_is_an_income_account(self):
|
||||
"""Tests if the Depreciation Expense Account gets credited and the Accumulated Depreciation Account gets debited when the former's an Income Account."""
|
||||
|
||||
depr_expense_account = frappe.get_doc("Account", "_Test Depreciations - _TC")
|
||||
depr_expense_account.root_type = "Income"
|
||||
depr_expense_account.parent_account = "Income - _TC"
|
||||
depr_expense_account.save()
|
||||
|
||||
asset = create_asset(
|
||||
item_code = "Macbook Pro",
|
||||
calculate_depreciation = 1,
|
||||
available_for_use_date = "2019-12-31",
|
||||
depreciation_start_date = "2020-12-31",
|
||||
frequency_of_depreciation = 12,
|
||||
total_number_of_depreciations = 3,
|
||||
expected_value_after_useful_life = 10000,
|
||||
submit = 1
|
||||
)
|
||||
|
||||
post_depreciation_entries(date="2021-06-01")
|
||||
asset.load_from_db()
|
||||
|
||||
je = frappe.get_doc("Journal Entry", asset.schedules[0].journal_entry)
|
||||
accounting_entries = [{"account": entry.account, "debit": entry.debit, "credit": entry.credit} for entry in je.accounts]
|
||||
|
||||
for entry in accounting_entries:
|
||||
if entry["account"] == "_Test Depreciations - _TC":
|
||||
self.assertTrue(entry["credit"])
|
||||
self.assertFalse(entry["debit"])
|
||||
else:
|
||||
self.assertTrue(entry["debit"])
|
||||
self.assertFalse(entry["credit"])
|
||||
|
||||
# resetting
|
||||
depr_expense_account.root_type = "Expense"
|
||||
depr_expense_account.parent_account = "Expenses - _TC"
|
||||
depr_expense_account.save()
|
||||
|
||||
def test_clear_depreciation_schedule(self):
|
||||
"""Tests if clear_depreciation_schedule() works as expected."""
|
||||
|
||||
|
@ -33,7 +33,7 @@ frappe.ui.form.on('Asset Category', {
|
||||
var d = locals[cdt][cdn];
|
||||
return {
|
||||
"filters": {
|
||||
"root_type": "Expense",
|
||||
"root_type": ["in", ["Expense", "Income"]],
|
||||
"is_group": 0,
|
||||
"company": d.company_name
|
||||
}
|
||||
|
@ -42,10 +42,10 @@ class AssetCategory(Document):
|
||||
|
||||
def validate_account_types(self):
|
||||
account_type_map = {
|
||||
'fixed_asset_account': { 'account_type': 'Fixed Asset' },
|
||||
'accumulated_depreciation_account': { 'account_type': 'Accumulated Depreciation' },
|
||||
'depreciation_expense_account': { 'root_type': 'Expense' },
|
||||
'capital_work_in_progress_account': { 'account_type': 'Capital Work in Progress' }
|
||||
'fixed_asset_account': {'account_type': ['Fixed Asset']},
|
||||
'accumulated_depreciation_account': {'account_type': ['Accumulated Depreciation']},
|
||||
'depreciation_expense_account': {'root_type': ['Expense', 'Income']},
|
||||
'capital_work_in_progress_account': {'account_type': ['Capital Work in Progress']}
|
||||
}
|
||||
for d in self.accounts:
|
||||
for fieldname in account_type_map.keys():
|
||||
@ -53,11 +53,11 @@ class AssetCategory(Document):
|
||||
selected_account = d.get(fieldname)
|
||||
key_to_match = next(iter(account_type_map.get(fieldname))) # acount_type or root_type
|
||||
selected_key_type = frappe.db.get_value('Account', selected_account, key_to_match)
|
||||
expected_key_type = account_type_map[fieldname][key_to_match]
|
||||
expected_key_types = account_type_map[fieldname][key_to_match]
|
||||
|
||||
if selected_key_type != expected_key_type:
|
||||
if selected_key_type not in expected_key_types:
|
||||
frappe.throw(_("Row #{}: {} of {} should be {}. Please modify the account or select a different account.")
|
||||
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_type)),
|
||||
.format(d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), frappe.bold(expected_key_types)),
|
||||
title=_("Invalid Account"))
|
||||
|
||||
def valide_cwip_account(self):
|
||||
|
0
erpnext/crm/doctype/crm_settings/__init__.py
Normal file
0
erpnext/crm/doctype/crm_settings/__init__.py
Normal file
8
erpnext/crm/doctype/crm_settings/crm_settings.js
Normal file
8
erpnext/crm/doctype/crm_settings/crm_settings.js
Normal file
@ -0,0 +1,8 @@
|
||||
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('CRM Settings', {
|
||||
// refresh: function(frm) {
|
||||
|
||||
// }
|
||||
});
|
114
erpnext/crm/doctype/crm_settings/crm_settings.json
Normal file
114
erpnext/crm/doctype/crm_settings/crm_settings.json
Normal file
@ -0,0 +1,114 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2021-09-09 17:03:22.754446",
|
||||
"description": "Settings for Selling Module",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Other",
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"section_break_5",
|
||||
"campaign_naming_by",
|
||||
"allow_lead_duplication_based_on_emails",
|
||||
"column_break_4",
|
||||
"create_event_on_next_contact_date",
|
||||
"auto_creation_of_contact",
|
||||
"opportunity_section",
|
||||
"close_opportunity_after_days",
|
||||
"column_break_9",
|
||||
"create_event_on_next_contact_date_opportunity",
|
||||
"quotation_section",
|
||||
"default_valid_till"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "campaign_naming_by",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Campaign Naming By",
|
||||
"options": "Campaign Name\nNaming Series"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_valid_till",
|
||||
"fieldtype": "Data",
|
||||
"label": "Default Quotation Validity Days"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Lead"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_lead_duplication_based_on_emails",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow Lead Duplication based on Emails"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "auto_creation_of_contact",
|
||||
"fieldtype": "Check",
|
||||
"label": "Auto Creation of Contact"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "create_event_on_next_contact_date",
|
||||
"fieldtype": "Check",
|
||||
"label": "Create Event on Next Contact Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "opportunity_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Opportunity"
|
||||
},
|
||||
{
|
||||
"default": "15",
|
||||
"description": "Auto close Opportunity Replied after the no. of days mentioned above",
|
||||
"fieldname": "close_opportunity_after_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Close Replied Opportunity After Days"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "create_event_on_next_contact_date_opportunity",
|
||||
"fieldtype": "Check",
|
||||
"label": "Create Event on Next Contact Date"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "quotation_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Quotation"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"migration_hash": "3ae78b12dd1c64d551736c6e82092f90",
|
||||
"modified": "2021-11-03 09:00:36.883496",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "CRM Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
9
erpnext/crm/doctype/crm_settings/crm_settings.py
Normal file
9
erpnext/crm/doctype/crm_settings/crm_settings.py
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class CRMSettings(Document):
|
||||
pass
|
9
erpnext/crm/doctype/crm_settings/test_crm_settings.py
Normal file
9
erpnext/crm/doctype/crm_settings/test_crm_settings.py
Normal file
@ -0,0 +1,9 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
|
||||
class TestCRMSettings(unittest.TestCase):
|
||||
pass
|
@ -11,6 +11,7 @@ from frappe.utils import (
|
||||
cint,
|
||||
comma_and,
|
||||
cstr,
|
||||
get_link_to_form,
|
||||
getdate,
|
||||
has_gravatar,
|
||||
nowdate,
|
||||
@ -91,13 +92,14 @@ class Lead(SellingController):
|
||||
self.contact_doc.save()
|
||||
|
||||
def add_calendar_event(self, opts=None, force=False):
|
||||
super(Lead, self).add_calendar_event({
|
||||
"owner": self.lead_owner,
|
||||
"starts_on": self.contact_date,
|
||||
"ends_on": self.ends_on or "",
|
||||
"subject": ('Contact ' + cstr(self.lead_name)),
|
||||
"description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
|
||||
}, force)
|
||||
if frappe.db.get_single_value('CRM Settings', 'create_event_on_next_contact_date'):
|
||||
super(Lead, self).add_calendar_event({
|
||||
"owner": self.lead_owner,
|
||||
"starts_on": self.contact_date,
|
||||
"ends_on": self.ends_on or "",
|
||||
"subject": ('Contact ' + cstr(self.lead_name)),
|
||||
"description": ('Contact ' + cstr(self.lead_name)) + (self.contact_by and ('. By : ' + cstr(self.contact_by)) or '')
|
||||
}, force)
|
||||
|
||||
def update_prospects(self):
|
||||
prospects = frappe.get_all('Prospect Lead', filters={'lead': self.name}, fields=['parent'])
|
||||
@ -108,12 +110,13 @@ class Lead(SellingController):
|
||||
def check_email_id_is_unique(self):
|
||||
if self.email_id:
|
||||
# validate email is unique
|
||||
duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
|
||||
duplicate_leads = [lead.name for lead in duplicate_leads]
|
||||
if not frappe.db.get_single_value('CRM Settings', 'allow_lead_duplication_based_on_emails'):
|
||||
duplicate_leads = frappe.get_all("Lead", filters={"email_id": self.email_id, "name": ["!=", self.name]})
|
||||
duplicate_leads = [frappe.bold(get_link_to_form('Lead', lead.name)) for lead in duplicate_leads]
|
||||
|
||||
if duplicate_leads:
|
||||
frappe.throw(_("Email Address must be unique, already exists for {0}")
|
||||
.format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
|
||||
if duplicate_leads:
|
||||
frappe.throw(_("Email Address must be unique, already exists for {0}")
|
||||
.format(comma_and(duplicate_leads)), frappe.DuplicateEntryError)
|
||||
|
||||
def on_trash(self):
|
||||
frappe.db.sql("""update `tabIssue` set lead='' where lead=%s""", self.name)
|
||||
@ -172,41 +175,42 @@ class Lead(SellingController):
|
||||
self.title = self.company_name or self.lead_name
|
||||
|
||||
def create_contact(self):
|
||||
if not self.lead_name:
|
||||
self.set_full_name()
|
||||
self.set_lead_name()
|
||||
if frappe.db.get_single_value('CRM Settings', 'auto_creation_of_contact'):
|
||||
if not self.lead_name:
|
||||
self.set_full_name()
|
||||
self.set_lead_name()
|
||||
|
||||
contact = frappe.new_doc("Contact")
|
||||
contact.update({
|
||||
"first_name": self.first_name or self.lead_name,
|
||||
"last_name": self.last_name,
|
||||
"salutation": self.salutation,
|
||||
"gender": self.gender,
|
||||
"designation": self.designation,
|
||||
"company_name": self.company_name,
|
||||
})
|
||||
|
||||
if self.email_id:
|
||||
contact.append("email_ids", {
|
||||
"email_id": self.email_id,
|
||||
"is_primary": 1
|
||||
contact = frappe.new_doc("Contact")
|
||||
contact.update({
|
||||
"first_name": self.first_name or self.lead_name,
|
||||
"last_name": self.last_name,
|
||||
"salutation": self.salutation,
|
||||
"gender": self.gender,
|
||||
"designation": self.designation,
|
||||
"company_name": self.company_name,
|
||||
})
|
||||
|
||||
if self.phone:
|
||||
contact.append("phone_nos", {
|
||||
"phone": self.phone,
|
||||
"is_primary_phone": 1
|
||||
})
|
||||
if self.email_id:
|
||||
contact.append("email_ids", {
|
||||
"email_id": self.email_id,
|
||||
"is_primary": 1
|
||||
})
|
||||
|
||||
if self.mobile_no:
|
||||
contact.append("phone_nos", {
|
||||
"phone": self.mobile_no,
|
||||
"is_primary_mobile_no":1
|
||||
})
|
||||
if self.phone:
|
||||
contact.append("phone_nos", {
|
||||
"phone": self.phone,
|
||||
"is_primary_phone": 1
|
||||
})
|
||||
|
||||
contact.insert(ignore_permissions=True)
|
||||
if self.mobile_no:
|
||||
contact.append("phone_nos", {
|
||||
"phone": self.mobile_no,
|
||||
"is_primary_mobile_no":1
|
||||
})
|
||||
|
||||
return contact
|
||||
contact.insert(ignore_permissions=True)
|
||||
|
||||
return contact
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_customer(source_name, target_doc=None):
|
||||
|
@ -8,6 +8,7 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.email.inbox import link_communication_to_document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.query_builder import DocType
|
||||
from frappe.utils import cint, cstr, flt, get_fullname
|
||||
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
@ -28,7 +29,6 @@ class Opportunity(TransactionBase):
|
||||
})
|
||||
|
||||
self.make_new_lead_if_required()
|
||||
|
||||
self.validate_item_details()
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.validate_cust_name()
|
||||
@ -70,21 +70,21 @@ class Opportunity(TransactionBase):
|
||||
"""Set lead against new opportunity"""
|
||||
if (not self.get("party_name")) and self.contact_email:
|
||||
# check if customer is already created agains the self.contact_email
|
||||
customer = frappe.db.sql("""select
|
||||
distinct `tabDynamic Link`.link_name as customer
|
||||
from
|
||||
`tabContact`,
|
||||
`tabDynamic Link`
|
||||
where `tabContact`.email_id='{0}'
|
||||
and
|
||||
`tabContact`.name=`tabDynamic Link`.parent
|
||||
and
|
||||
ifnull(`tabDynamic Link`.link_name, '')<>''
|
||||
and
|
||||
`tabDynamic Link`.link_doctype='Customer'
|
||||
""".format(self.contact_email), as_dict=True)
|
||||
if customer and customer[0].customer:
|
||||
self.party_name = customer[0].customer
|
||||
dynamic_link, contact = DocType("Dynamic Link"), DocType("Contact")
|
||||
customer = frappe.qb.from_(
|
||||
dynamic_link
|
||||
).join(
|
||||
contact
|
||||
).on(
|
||||
(contact.name == dynamic_link.parent)
|
||||
& (dynamic_link.link_doctype == "Customer")
|
||||
& (contact.email_id == self.contact_email)
|
||||
).select(
|
||||
dynamic_link.link_name
|
||||
).distinct().run(as_dict=True)
|
||||
|
||||
if customer and customer[0].link_name:
|
||||
self.party_name = customer[0].link_name
|
||||
self.opportunity_from = "Customer"
|
||||
return
|
||||
|
||||
@ -191,30 +191,31 @@ class Opportunity(TransactionBase):
|
||||
self.add_calendar_event()
|
||||
|
||||
def add_calendar_event(self, opts=None, force=False):
|
||||
if not opts:
|
||||
opts = frappe._dict()
|
||||
if frappe.db.get_single_value('CRM Settings', 'create_event_on_next_contact_date_opportunity'):
|
||||
if not opts:
|
||||
opts = frappe._dict()
|
||||
|
||||
opts.description = ""
|
||||
opts.contact_date = self.contact_date
|
||||
opts.description = ""
|
||||
opts.contact_date = self.contact_date
|
||||
|
||||
if self.party_name and self.opportunity_from == 'Customer':
|
||||
if self.contact_person:
|
||||
opts.description = 'Contact '+cstr(self.contact_person)
|
||||
else:
|
||||
opts.description = 'Contact customer '+cstr(self.party_name)
|
||||
elif self.party_name and self.opportunity_from == 'Lead':
|
||||
if self.contact_display:
|
||||
opts.description = 'Contact '+cstr(self.contact_display)
|
||||
else:
|
||||
opts.description = 'Contact lead '+cstr(self.party_name)
|
||||
if self.party_name and self.opportunity_from == 'Customer':
|
||||
if self.contact_person:
|
||||
opts.description = 'Contact '+cstr(self.contact_person)
|
||||
else:
|
||||
opts.description = 'Contact customer '+cstr(self.party_name)
|
||||
elif self.party_name and self.opportunity_from == 'Lead':
|
||||
if self.contact_display:
|
||||
opts.description = 'Contact '+cstr(self.contact_display)
|
||||
else:
|
||||
opts.description = 'Contact lead '+cstr(self.party_name)
|
||||
|
||||
opts.subject = opts.description
|
||||
opts.description += '. By : ' + cstr(self.contact_by)
|
||||
opts.subject = opts.description
|
||||
opts.description += '. By : ' + cstr(self.contact_by)
|
||||
|
||||
if self.to_discuss:
|
||||
opts.description += ' To Discuss : ' + cstr(self.to_discuss)
|
||||
if self.to_discuss:
|
||||
opts.description += ' To Discuss : ' + cstr(self.to_discuss)
|
||||
|
||||
super(Opportunity, self).add_calendar_event(opts, force)
|
||||
super(Opportunity, self).add_calendar_event(opts, force)
|
||||
|
||||
def validate_item_details(self):
|
||||
if not self.get('items'):
|
||||
@ -363,7 +364,7 @@ def set_multiple_status(names, status):
|
||||
|
||||
def auto_close_opportunity():
|
||||
""" auto close the `Replied` Opportunities after 7 days """
|
||||
auto_close_after_days = frappe.db.get_single_value("Selling Settings", "close_opportunity_after_days") or 15
|
||||
auto_close_after_days = frappe.db.get_single_value("CRM Settings", "close_opportunity_after_days") or 15
|
||||
|
||||
opportunities = frappe.db.sql(""" select name from tabOpportunity where status='Replied' and
|
||||
modified<DATE_SUB(CURDATE(), INTERVAL %s DAY) """, (auto_close_after_days), as_dict=True)
|
||||
|
@ -20,7 +20,6 @@
|
||||
"configuration_cb",
|
||||
"shipping_account_head",
|
||||
"section_break_12",
|
||||
"nexus_address",
|
||||
"nexus"
|
||||
],
|
||||
"fields": [
|
||||
@ -87,15 +86,11 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "nexus",
|
||||
"fieldname": "section_break_12",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Nexus List"
|
||||
},
|
||||
{
|
||||
"fieldname": "nexus_address",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Nexus Address"
|
||||
},
|
||||
{
|
||||
"fieldname": "nexus",
|
||||
"fieldtype": "Table",
|
||||
@ -107,20 +102,21 @@
|
||||
"fieldname": "configuration_cb",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-11-08 18:02:29.232090",
|
||||
"migration_hash": "8ca1ea3309ed28547b19da8e6e27e96f",
|
||||
"modified": "2021-11-30 11:17:24.647979",
|
||||
"modified_by": "Administrator",
|
||||
"module": "ERPNext Integrations",
|
||||
"name": "TaxJar Settings",
|
||||
|
@ -16,9 +16,9 @@ from erpnext.erpnext_integrations.taxjar_integration import get_client
|
||||
class TaxJarSettings(Document):
|
||||
|
||||
def on_update(self):
|
||||
TAXJAR_CREATE_TRANSACTIONS = frappe.db.get_single_value("TaxJar Settings", "taxjar_create_transactions")
|
||||
TAXJAR_CALCULATE_TAX = frappe.db.get_single_value("TaxJar Settings", "taxjar_calculate_tax")
|
||||
TAXJAR_SANDBOX_MODE = frappe.db.get_single_value("TaxJar Settings", "is_sandbox")
|
||||
TAXJAR_CREATE_TRANSACTIONS = self.taxjar_create_transactions
|
||||
TAXJAR_CALCULATE_TAX = self.taxjar_calculate_tax
|
||||
TAXJAR_SANDBOX_MODE = self.is_sandbox
|
||||
|
||||
fields_already_exist = frappe.db.exists('Custom Field', {'dt': ('in', ['Item','Sales Invoice Item']), 'fieldname':'product_tax_category'})
|
||||
fields_hidden = frappe.get_value('Custom Field', {'dt': ('in', ['Sales Invoice Item'])}, 'hidden')
|
||||
|
@ -323,10 +323,14 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No
|
||||
target.maintenance_schedule = source.name
|
||||
target.maintenance_schedule_detail = s_id
|
||||
|
||||
def update_sales(source, target, parent):
|
||||
def update_sales_and_serial(source, target, parent):
|
||||
sales_person = frappe.db.get_value('Maintenance Schedule Detail', s_id, 'sales_person')
|
||||
target.service_person = sales_person
|
||||
target.serial_no = ''
|
||||
serial_nos = get_serial_nos(target.serial_no)
|
||||
if len(serial_nos) == 1:
|
||||
target.serial_no = serial_nos[0]
|
||||
else:
|
||||
target.serial_no = ''
|
||||
|
||||
doclist = get_mapped_doc("Maintenance Schedule", source_name, {
|
||||
"Maintenance Schedule": {
|
||||
@ -342,7 +346,7 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No
|
||||
"Maintenance Schedule Item": {
|
||||
"doctype": "Maintenance Visit Purpose",
|
||||
"condition": lambda doc: doc.item_name == item_name,
|
||||
"postprocess": update_sales
|
||||
"postprocess": update_sales_and_serial
|
||||
}
|
||||
}, target_doc)
|
||||
|
||||
|
@ -278,6 +278,7 @@ erpnext.patches.v13_0.update_tds_check_field #3
|
||||
erpnext.patches.v13_0.add_custom_field_for_south_africa #2
|
||||
erpnext.patches.v13_0.update_recipient_email_digest
|
||||
erpnext.patches.v13_0.shopify_deprecation_warning
|
||||
erpnext.patches.v13_0.remove_bad_selling_defaults
|
||||
erpnext.patches.v13_0.migrate_stripe_api
|
||||
erpnext.patches.v13_0.reset_clearance_date_for_intracompany_payment_entries
|
||||
erpnext.patches.v13_0.einvoicing_deprecation_warning
|
||||
@ -312,4 +313,5 @@ erpnext.patches.v13_0.update_category_in_ltds_certificate
|
||||
erpnext.patches.v13_0.create_pan_field_for_india #2
|
||||
erpnext.patches.v14_0.delete_hub_doctypes
|
||||
erpnext.patches.v13_0.create_ksa_vat_custom_fields
|
||||
erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
|
||||
erpnext.patches.v14_0.migrate_crm_settings
|
||||
erpnext.patches.v14_0.add_default_exit_questionnaire_notification_template
|
||||
|
15
erpnext/patches/v13_0/remove_bad_selling_defaults.py
Normal file
15
erpnext/patches/v13_0/remove_bad_selling_defaults.py
Normal file
@ -0,0 +1,15 @@
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
||||
|
||||
def execute():
|
||||
selling_settings = frappe.get_single("Selling Settings")
|
||||
|
||||
if selling_settings.customer_group in (_("All Customer Groups"), "All Customer Groups"):
|
||||
selling_settings.customer_group = None
|
||||
|
||||
if selling_settings.territory in (_("All Territories"), "All Territories"):
|
||||
selling_settings.territory = None
|
||||
|
||||
selling_settings.flags.ignore_mandatory=True
|
||||
selling_settings.save(ignore_permissions=True)
|
16
erpnext/patches/v14_0/migrate_crm_settings.py
Normal file
16
erpnext/patches/v14_0/migrate_crm_settings.py
Normal file
@ -0,0 +1,16 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
settings = frappe.db.get_value('Selling Settings', 'Selling Settings', [
|
||||
'campaign_naming_by',
|
||||
'close_opportunity_after_days',
|
||||
'default_valid_till'
|
||||
], as_dict=True)
|
||||
|
||||
frappe.reload_doc('crm', 'doctype', 'crm_settings')
|
||||
frappe.db.set_value('CRM Settings', 'CRM Settings', {
|
||||
'campaign_naming_by': settings.campaign_naming_by,
|
||||
'close_opportunity_after_days': settings.close_opportunity_after_days,
|
||||
'default_valid_till': settings.default_valid_till
|
||||
})
|
@ -11,11 +11,6 @@
|
||||
"customer_group",
|
||||
"column_break_4",
|
||||
"territory",
|
||||
"crm_settings_section",
|
||||
"campaign_naming_by",
|
||||
"default_valid_till",
|
||||
"column_break_9",
|
||||
"close_opportunity_after_days",
|
||||
"item_price_settings_section",
|
||||
"selling_price_list",
|
||||
"maintain_same_rate_action",
|
||||
@ -43,13 +38,6 @@
|
||||
"label": "Customer Naming By",
|
||||
"options": "Customer Name\nNaming Series\nAuto Name"
|
||||
},
|
||||
{
|
||||
"fieldname": "campaign_naming_by",
|
||||
"fieldtype": "Select",
|
||||
"in_list_view": 1,
|
||||
"label": "Campaign Naming By",
|
||||
"options": "Campaign Name\nNaming Series\nAuto Name"
|
||||
},
|
||||
{
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
@ -71,18 +59,6 @@
|
||||
"label": "Default Price List",
|
||||
"options": "Price List"
|
||||
},
|
||||
{
|
||||
"default": "15",
|
||||
"description": "Auto close Opportunity after the no. of days mentioned above",
|
||||
"fieldname": "close_opportunity_after_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Close Opportunity After Days"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_valid_till",
|
||||
"fieldtype": "Data",
|
||||
"label": "Default Quotation Validity Days"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break"
|
||||
@ -169,15 +145,6 @@
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "crm_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "CRM Settings"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "item_price_settings_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -204,7 +171,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-09-08 19:38:10.175989",
|
||||
"modified": "2021-09-13 12:32:17.004404",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Selling Settings",
|
||||
|
@ -8,7 +8,6 @@ import frappe
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
from frappe.utils.nestedset import get_root_of
|
||||
|
||||
|
||||
class SellingSettings(Document):
|
||||
@ -37,9 +36,3 @@ class SellingSettings(Document):
|
||||
editable_bundle_item_rates = cint(self.editable_bundle_item_rates)
|
||||
|
||||
make_property_setter("Packed Item", "rate", "read_only", not(editable_bundle_item_rates), "Check", validate_fields_for_doctype=False)
|
||||
|
||||
def set_default_customer_group_and_territory(self):
|
||||
if not self.customer_group:
|
||||
self.customer_group = get_root_of('Customer Group')
|
||||
if not self.territory:
|
||||
self.territory = get_root_of('Territory')
|
||||
|
@ -308,7 +308,6 @@ def set_more_defaults():
|
||||
|
||||
def update_selling_defaults():
|
||||
selling_settings = frappe.get_doc("Selling Settings")
|
||||
selling_settings.set_default_customer_group_and_territory()
|
||||
selling_settings.cust_master_name = "Customer Name"
|
||||
selling_settings.so_required = "No"
|
||||
selling_settings.dn_required = "No"
|
||||
|
@ -53,6 +53,7 @@ def before_tests():
|
||||
|
||||
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 0)
|
||||
enable_all_roles_and_domains()
|
||||
set_defaults_for_tests()
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
@ -127,6 +128,14 @@ def enable_all_roles_and_domains():
|
||||
[d.name for d in domains])
|
||||
add_all_roles_to('Administrator')
|
||||
|
||||
def set_defaults_for_tests():
|
||||
from frappe.utils.nestedset import get_root_of
|
||||
|
||||
selling_settings = frappe.get_single("Selling Settings")
|
||||
selling_settings.customer_group = get_root_of("Customer Group")
|
||||
selling_settings.territory = get_root_of("Territory")
|
||||
selling_settings.save()
|
||||
|
||||
|
||||
def insert_record(records):
|
||||
for r in records:
|
||||
|
@ -10,7 +10,7 @@
|
||||
"idx": 0,
|
||||
"label": "ERPNext Settings",
|
||||
"links": [],
|
||||
"modified": "2021-10-26 21:32:55.323591",
|
||||
"modified": "2021-11-05 21:32:55.323591",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "ERPNext Settings",
|
||||
@ -123,6 +123,13 @@
|
||||
"label": "Products Settings",
|
||||
"link_to": "Products Settings",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"icon": "crm",
|
||||
"label": "CRM Settings",
|
||||
"link_to": "CRM Settings",
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"title": "ERPNext Settings"
|
||||
|
@ -22,7 +22,7 @@ def boot_session(bootinfo):
|
||||
'customer_group')
|
||||
bootinfo.sysdefaults.allow_stale = cint(frappe.db.get_single_value('Accounts Settings',
|
||||
'allow_stale'))
|
||||
bootinfo.sysdefaults.quotation_valid_till = cint(frappe.db.get_single_value('Selling Settings',
|
||||
bootinfo.sysdefaults.quotation_valid_till = cint(frappe.db.get_single_value('CRM Settings',
|
||||
'default_valid_till'))
|
||||
|
||||
# if no company, show a dialog box to create a new company
|
||||
|
@ -534,8 +534,6 @@ class TestItem(ERPNextTestCase):
|
||||
|
||||
def test_index_creation(self):
|
||||
"check if index is getting created in db"
|
||||
from erpnext.stock.doctype.item.item import on_doctype_update
|
||||
on_doctype_update()
|
||||
|
||||
indices = frappe.db.sql("show index from tabItem", as_dict=1)
|
||||
expected_columns = {"item_code", "item_name", "item_group", "route"}
|
||||
|
Loading…
Reference in New Issue
Block a user