style: bulk format code with black

v13 port because otherwise backports will result in conflicts always
This commit is contained in:
Ankush Menat
2022-03-29 17:29:34 +05:30
parent 7cc84dcbb4
commit c07713b860
1555 changed files with 96709 additions and 66138 deletions

View File

@@ -1,57 +1,48 @@
from frappe import _
doctype_rule_map = {
'Item': {
'points': 5,
'for_doc_event': 'New'
"Item": {"points": 5, "for_doc_event": "New"},
"Customer": {"points": 5, "for_doc_event": "New"},
"Supplier": {"points": 5, "for_doc_event": "New"},
"Lead": {"points": 2, "for_doc_event": "New"},
"Opportunity": {
"points": 10,
"for_doc_event": "Custom",
"condition": 'doc.status=="Converted"',
"rule_name": _("On Converting Opportunity"),
"user_field": "converted_by",
},
'Customer': {
'points': 5,
'for_doc_event': 'New'
"Sales Order": {
"points": 10,
"for_doc_event": "Submit",
"rule_name": _("On Sales Order Submission"),
"user_field": "modified_by",
},
'Supplier': {
'points': 5,
'for_doc_event': 'New'
"Purchase Order": {
"points": 10,
"for_doc_event": "Submit",
"rule_name": _("On Purchase Order Submission"),
"user_field": "modified_by",
},
'Lead': {
'points': 2,
'for_doc_event': 'New'
"Task": {
"points": 5,
"condition": 'doc.status == "Completed"',
"rule_name": _("On Task Completion"),
"user_field": "completed_by",
},
'Opportunity': {
'points': 10,
'for_doc_event': 'Custom',
'condition': 'doc.status=="Converted"',
'rule_name': _('On Converting Opportunity'),
'user_field': 'converted_by'
},
'Sales Order': {
'points': 10,
'for_doc_event': 'Submit',
'rule_name': _('On Sales Order Submission'),
'user_field': 'modified_by'
},
'Purchase Order': {
'points': 10,
'for_doc_event': 'Submit',
'rule_name': _('On Purchase Order Submission'),
'user_field': 'modified_by'
},
'Task': {
'points': 5,
'condition': 'doc.status == "Completed"',
'rule_name': _('On Task Completion'),
'user_field': 'completed_by'
}
}
def get_default_energy_point_rules():
return [{
'doctype': 'Energy Point Rule',
'reference_doctype': doctype,
'for_doc_event': rule.get('for_doc_event') or 'Custom',
'condition': rule.get('condition'),
'rule_name': rule.get('rule_name') or _('On {0} Creation').format(doctype),
'points': rule.get('points'),
'user_field': rule.get('user_field') or 'owner'
} for doctype, rule in doctype_rule_map.items()]
return [
{
"doctype": "Energy Point Rule",
"reference_doctype": doctype,
"for_doc_event": rule.get("for_doc_event") or "Custom",
"condition": rule.get("condition"),
"rule_name": rule.get("rule_name") or _("On {0} Creation").format(doctype),
"points": rule.get("points"),
"user_field": rule.get("user_field") or "owner",
}
for doctype, rule in doctype_rule_map.items()
]

View File

@@ -1,26 +1,31 @@
from frappe import _
doctype_list = [
'Purchase Receipt',
'Purchase Invoice',
'Quotation',
'Sales Order',
'Delivery Note',
'Sales Invoice'
"Purchase Receipt",
"Purchase Invoice",
"Quotation",
"Sales Order",
"Delivery Note",
"Sales Invoice",
]
def get_message(doctype):
return _("{0} has been submitted successfully").format(_(doctype))
def get_first_success_message(doctype):
return get_message(doctype)
def get_default_success_action():
return [{
'doctype': 'Success Action',
'ref_doctype': doctype,
'message': get_message(doctype),
'first_success_message': get_first_success_message(doctype),
'next_actions': 'new\nprint\nemail'
} for doctype in doctype_list]
return [
{
"doctype": "Success Action",
"ref_doctype": doctype,
"message": get_message(doctype),
"first_success_message": get_first_success_message(doctype),
"next_actions": "new\nprint\nemail",
}
for doctype in doctype_list
]

View File

@@ -12,90 +12,121 @@ from erpnext.utilities.transaction_base import TransactionBase
class AuthorizationControl(TransactionBase):
def get_appr_user_role(self, det, doctype_name, total, based_on, condition, item, company):
amt_list, appr_users, appr_roles = [], [], []
users, roles = '',''
users, roles = "", ""
if det:
for x in det:
amt_list.append(flt(x[0]))
max_amount = max(amt_list)
app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
app_dtl = frappe.db.sql(
"""select approving_user, approving_role from `tabAuthorization Rule`
where transaction = %s and (value = %s or value > %s)
and docstatus != 2 and based_on = %s and company = %s %s""" %
('%s', '%s', '%s', '%s', '%s', condition),
(doctype_name, flt(max_amount), total, based_on, company))
and docstatus != 2 and based_on = %s and company = %s %s"""
% ("%s", "%s", "%s", "%s", "%s", condition),
(doctype_name, flt(max_amount), total, based_on, company),
)
if not app_dtl:
app_dtl = frappe.db.sql("""select approving_user, approving_role from `tabAuthorization Rule`
app_dtl = frappe.db.sql(
"""select approving_user, approving_role from `tabAuthorization Rule`
where transaction = %s and (value = %s or value > %s) and docstatus != 2
and based_on = %s and ifnull(company,'') = '' %s""" %
('%s', '%s', '%s', '%s', condition), (doctype_name, flt(max_amount), total, based_on))
and based_on = %s and ifnull(company,'') = '' %s"""
% ("%s", "%s", "%s", "%s", condition),
(doctype_name, flt(max_amount), total, based_on),
)
for d in app_dtl:
if(d[0]): appr_users.append(d[0])
if(d[1]): appr_roles.append(d[1])
if d[0]:
appr_users.append(d[0])
if d[1]:
appr_roles.append(d[1])
if not has_common(appr_roles, frappe.get_roles()) and not has_common(appr_users, [session['user']]):
if not has_common(appr_roles, frappe.get_roles()) and not has_common(
appr_users, [session["user"]]
):
frappe.msgprint(_("Not authroized since {0} exceeds limits").format(_(based_on)))
frappe.throw(_("Can be approved by {0}").format(comma_or(appr_roles + appr_users)))
def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item = ''):
def validate_auth_rule(self, doctype_name, total, based_on, cond, company, item=""):
chk = 1
add_cond1,add_cond2 = '',''
if based_on == 'Itemwise Discount':
add_cond1, add_cond2 = "", ""
if based_on == "Itemwise Discount":
add_cond1 += " and master_name = " + frappe.db.escape(cstr(item))
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
itemwise_exists = frappe.db.sql(
"""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s
and based_on = %s and company = %s and docstatus != 2 %s %s""" %
('%s', '%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on, company))
and based_on = %s and company = %s and docstatus != 2 %s %s"""
% ("%s", "%s", "%s", "%s", cond, add_cond1),
(doctype_name, total, based_on, company),
)
if not itemwise_exists:
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
itemwise_exists = frappe.db.sql(
"""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s and based_on = %s
and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
('%s', '%s', '%s', cond, add_cond1), (doctype_name, total, based_on))
and ifnull(company,'') = '' and docstatus != 2 %s %s"""
% ("%s", "%s", "%s", cond, add_cond1),
(doctype_name, total, based_on),
)
if itemwise_exists:
self.get_appr_user_role(itemwise_exists, doctype_name, total, based_on, cond+add_cond1, item,company)
self.get_appr_user_role(
itemwise_exists, doctype_name, total, based_on, cond + add_cond1, item, company
)
chk = 0
if chk == 1:
if based_on == 'Itemwise Discount':
if based_on == "Itemwise Discount":
add_cond2 += " and ifnull(master_name,'') = ''"
appr = frappe.db.sql("""select value from `tabAuthorization Rule`
appr = frappe.db.sql(
"""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s and based_on = %s
and company = %s and docstatus != 2 %s %s""" %
('%s', '%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on, company))
and company = %s and docstatus != 2 %s %s"""
% ("%s", "%s", "%s", "%s", cond, add_cond2),
(doctype_name, total, based_on, company),
)
if not appr:
appr = frappe.db.sql("""select value from `tabAuthorization Rule`
appr = frappe.db.sql(
"""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s and based_on = %s
and ifnull(company,'') = '' and docstatus != 2 %s %s""" %
('%s', '%s', '%s', cond, add_cond2), (doctype_name, total, based_on))
and ifnull(company,'') = '' and docstatus != 2 %s %s"""
% ("%s", "%s", "%s", cond, add_cond2),
(doctype_name, total, based_on),
)
self.get_appr_user_role(appr, doctype_name, total, based_on, cond+add_cond2, item, company)
self.get_appr_user_role(appr, doctype_name, total, based_on, cond + add_cond2, item, company)
def bifurcate_based_on_type(self, doctype_name, total, av_dis, based_on, doc_obj, val, company):
add_cond = ''
add_cond = ""
auth_value = av_dis
if val == 1: add_cond += " and system_user = {}".format(frappe.db.escape(session['user']))
elif val == 2: add_cond += " and system_role IN %s" % ("('"+"','".join(frappe.get_roles())+"')")
else: add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
if val == 1:
add_cond += " and system_user = {}".format(frappe.db.escape(session["user"]))
elif val == 2:
add_cond += " and system_role IN %s" % ("('" + "','".join(frappe.get_roles()) + "')")
else:
add_cond += " and ifnull(system_user,'') = '' and ifnull(system_role,'') = ''"
if based_on == 'Grand Total': auth_value = total
elif based_on == 'Customerwise Discount':
if based_on == "Grand Total":
auth_value = total
elif based_on == "Customerwise Discount":
if doc_obj:
if doc_obj.doctype == 'Sales Invoice': customer = doc_obj.customer
else: customer = doc_obj.customer_name
if doc_obj.doctype == "Sales Invoice":
customer = doc_obj.customer
else:
customer = doc_obj.customer_name
add_cond = " and master_name = {}".format(frappe.db.escape(customer))
if based_on == 'Itemwise Discount':
if based_on == "Itemwise Discount":
if doc_obj:
for t in doc_obj.get("items"):
self.validate_auth_rule(doctype_name, t.discount_percentage, based_on, add_cond, company,t.item_code )
self.validate_auth_rule(
doctype_name, t.discount_percentage, based_on, add_cond, company, t.item_code
)
else:
self.validate_auth_rule(doctype_name, auth_value, based_on, add_cond, company)
def validate_approving_authority(self, doctype_name,company, total, doc_obj = ''):
def validate_approving_authority(self, doctype_name, company, total, doc_obj=""):
if not frappe.db.count("Authorization Rule"):
return
@@ -109,56 +140,85 @@ class AuthorizationControl(TransactionBase):
if doc_obj.get("discount_amount"):
base_rate -= flt(doc_obj.discount_amount)
if price_list_rate: av_dis = 100 - flt(base_rate * 100 / price_list_rate)
if price_list_rate:
av_dis = 100 - flt(base_rate * 100 / price_list_rate)
final_based_on = ['Grand Total','Average Discount','Customerwise Discount','Itemwise Discount']
final_based_on = [
"Grand Total",
"Average Discount",
"Customerwise Discount",
"Itemwise Discount",
]
# Check for authorization set for individual user
based_on = [x[0] for x in frappe.db.sql("""select distinct based_on from `tabAuthorization Rule`
based_on = [
x[0]
for x in frappe.db.sql(
"""select distinct based_on from `tabAuthorization Rule`
where transaction = %s and system_user = %s
and (company = %s or ifnull(company,'')='') and docstatus != 2""",
(doctype_name, session['user'], company))]
(doctype_name, session["user"], company),
)
]
for d in based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 1, company)
# Remove user specific rules from global authorization rules
for r in based_on:
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
if r in final_based_on and r != "Itemwise Discount":
final_based_on.remove(r)
# Check for authorization set on particular roles
based_on = [x[0] for x in frappe.db.sql("""select based_on
based_on = [
x[0]
for x in frappe.db.sql(
"""select based_on
from `tabAuthorization Rule`
where transaction = %s and system_role IN (%s) and based_on IN (%s)
and (company = %s or ifnull(company,'')='')
and docstatus != 2
""" % ('%s', "'"+"','".join(frappe.get_roles())+"'", "'"+"','".join(final_based_on)+"'", '%s'), (doctype_name, company))]
"""
% (
"%s",
"'" + "','".join(frappe.get_roles()) + "'",
"'" + "','".join(final_based_on) + "'",
"%s",
),
(doctype_name, company),
)
]
for d in based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, d, doc_obj, 2, company)
# Remove role specific rules from global authorization rules
for r in based_on:
if r in final_based_on and r != 'Itemwise Discount': final_based_on.remove(r)
if r in final_based_on and r != "Itemwise Discount":
final_based_on.remove(r)
# Check for global authorization
for g in final_based_on:
self.bifurcate_based_on_type(doctype_name, total, av_dis, g, doc_obj, 0, company)
def get_value_based_rule(self,doctype_name,employee,total_claimed_amount,company):
val_lst =[]
val = frappe.db.sql("""select value from `tabAuthorization Rule`
def get_value_based_rule(self, doctype_name, employee, total_claimed_amount, company):
val_lst = []
val = frappe.db.sql(
"""select value from `tabAuthorization Rule`
where transaction=%s and (to_emp=%s or
to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)< %s and company = %s and docstatus!=2""",
(doctype_name,employee,employee,total_claimed_amount,company))
(doctype_name, employee, employee, total_claimed_amount, company),
)
if not val:
val = frappe.db.sql("""select value from `tabAuthorization Rule`
val = frappe.db.sql(
"""select value from `tabAuthorization Rule`
where transaction=%s and (to_emp=%s or
to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)< %s and ifnull(company,'') = '' and docstatus!=2""",
(doctype_name, employee, employee, total_claimed_amount))
(doctype_name, employee, employee, total_claimed_amount),
)
if val:
val_lst = [y[0] for y in val]
@@ -166,64 +226,83 @@ class AuthorizationControl(TransactionBase):
val_lst.append(0)
max_val = max(val_lst)
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
rule = frappe.db.sql(
"""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule`
where transaction=%s and company = %s
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)= %s and docstatus!=2""",
(doctype_name,company,employee,employee,flt(max_val)), as_dict=1)
(doctype_name, company, employee, employee, flt(max_val)),
as_dict=1,
)
if not rule:
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
rule = frappe.db.sql(
"""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule`
where transaction=%s and ifnull(company,'') = ''
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(value,0)= %s and docstatus!=2""",
(doctype_name,employee,employee,flt(max_val)), as_dict=1)
(doctype_name, employee, employee, flt(max_val)),
as_dict=1,
)
return rule
# related to payroll module only
def get_approver_name(self, doctype_name, total, doc_obj=''):
app_user=[]
app_specific_user =[]
rule ={}
def get_approver_name(self, doctype_name, total, doc_obj=""):
app_user = []
app_specific_user = []
rule = {}
if doc_obj:
if doctype_name == 'Expense Claim':
rule = self.get_value_based_rule(doctype_name, doc_obj.employee,
doc_obj.total_claimed_amount, doc_obj.company)
elif doctype_name == 'Appraisal':
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
if doctype_name == "Expense Claim":
rule = self.get_value_based_rule(
doctype_name, doc_obj.employee, doc_obj.total_claimed_amount, doc_obj.company
)
elif doctype_name == "Appraisal":
rule = frappe.db.sql(
"""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule` where transaction=%s
and (to_emp=%s or to_designation IN (select designation from `tabEmployee` where name=%s))
and company = %s and docstatus!=2""",
(doctype_name,doc_obj.employee, doc_obj.employee, doc_obj.company),as_dict=1)
(doctype_name, doc_obj.employee, doc_obj.employee, doc_obj.company),
as_dict=1,
)
if not rule:
rule = frappe.db.sql("""select name, to_emp, to_designation, approving_role, approving_user
rule = frappe.db.sql(
"""select name, to_emp, to_designation, approving_role, approving_user
from `tabAuthorization Rule`
where transaction=%s and (to_emp=%s or
to_designation IN (select designation from `tabEmployee` where name=%s))
and ifnull(company,'') = '' and docstatus!=2""",
(doctype_name,doc_obj.employee, doc_obj.employee), as_dict=1)
(doctype_name, doc_obj.employee, doc_obj.employee),
as_dict=1,
)
if rule:
for m in rule:
if m['to_emp'] or m['to_designation']:
if m['approving_user']:
app_specific_user.append(m['approving_user'])
elif m['approving_role']:
user_lst = [z[0] for z in frappe.db.sql("""select distinct t1.name
if m["to_emp"] or m["to_designation"]:
if m["approving_user"]:
app_specific_user.append(m["approving_user"])
elif m["approving_role"]:
user_lst = [
z[0]
for z in frappe.db.sql(
"""select distinct t1.name
from `tabUser` t1, `tabHas Role` t2 where t2.role=%s
and t2.parent=t1.name and t1.name !='Administrator'
and t1.name != 'Guest' and t1.docstatus !=2""", m['approving_role'])]
and t1.name != 'Guest' and t1.docstatus !=2""",
m["approving_role"],
)
]
for x in user_lst:
if not x in app_user:
app_user.append(x)
if len(app_specific_user) >0:
if len(app_specific_user) > 0:
return app_specific_user
else:
return app_user

View File

@@ -10,40 +10,58 @@ from frappe.utils import cstr, flt
class AuthorizationRule(Document):
def check_duplicate_entry(self):
exists = frappe.db.sql("""select name, docstatus from `tabAuthorization Rule`
exists = frappe.db.sql(
"""select name, docstatus from `tabAuthorization Rule`
where transaction = %s and based_on = %s and system_user = %s
and system_role = %s and approving_user = %s and approving_role = %s
and to_emp =%s and to_designation=%s and name != %s""",
(self.transaction, self.based_on, cstr(self.system_user),
cstr(self.system_role), cstr(self.approving_user),
cstr(self.approving_role), cstr(self.to_emp),
cstr(self.to_designation), self.name))
auth_exists = exists and exists[0][0] or ''
(
self.transaction,
self.based_on,
cstr(self.system_user),
cstr(self.system_role),
cstr(self.approving_user),
cstr(self.approving_role),
cstr(self.to_emp),
cstr(self.to_designation),
self.name,
),
)
auth_exists = exists and exists[0][0] or ""
if auth_exists:
frappe.throw(_("Duplicate Entry. Please check Authorization Rule {0}").format(auth_exists))
def validate_rule(self):
if self.transaction != 'Appraisal':
if self.transaction != "Appraisal":
if not self.approving_role and not self.approving_user:
frappe.throw(_("Please enter Approving Role or Approving User"))
elif self.system_user and self.system_user == self.approving_user:
frappe.throw(_("Approving User cannot be same as user the rule is Applicable To"))
elif self.system_role and self.system_role == self.approving_role:
frappe.throw(_("Approving Role cannot be same as role the rule is Applicable To"))
elif self.transaction in ['Purchase Order', 'Purchase Receipt', \
'Purchase Invoice', 'Stock Entry'] and self.based_on \
in ['Average Discount', 'Customerwise Discount', 'Itemwise Discount']:
frappe.throw(_("Cannot set authorization on basis of Discount for {0}").format(self.transaction))
elif self.based_on == 'Average Discount' and flt(self.value) > 100.00:
elif self.transaction in [
"Purchase Order",
"Purchase Receipt",
"Purchase Invoice",
"Stock Entry",
] and self.based_on in [
"Average Discount",
"Customerwise Discount",
"Itemwise Discount",
]:
frappe.throw(
_("Cannot set authorization on basis of Discount for {0}").format(self.transaction)
)
elif self.based_on == "Average Discount" and flt(self.value) > 100.00:
frappe.throw(_("Discount must be less than 100"))
elif self.based_on == 'Customerwise Discount' and not self.master_name:
elif self.based_on == "Customerwise Discount" and not self.master_name:
frappe.throw(_("Customer required for 'Customerwise Discount'"))
else:
if self.transaction == 'Appraisal':
if self.transaction == "Appraisal":
self.based_on = "Not Applicable"
def validate(self):
self.check_duplicate_entry()
self.validate_rule()
if not self.value: self.value = 0.0
if not self.value:
self.value = 0.0

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('Authorization Rule')
class TestAuthorizationRule(unittest.TestCase):
pass

View File

@@ -11,6 +11,7 @@ from frappe.model.document import Document
class Brand(Document):
pass
def get_brand_defaults(item, company):
item = frappe.get_cached_doc("Item", item)
if item.brand:

View File

@@ -3,4 +3,4 @@
import frappe
test_records = frappe.get_test_records('Brand')
test_records = frappe.get_test_records("Brand")

View File

@@ -21,7 +21,7 @@ from erpnext.setup.setup_wizard.operations.taxes_setup import setup_taxes_and_ch
class Company(NestedSet):
nsm_parent_field = 'parent_company'
nsm_parent_field = "parent_company"
def onload(self):
load_address_and_contact(self, "company")
@@ -29,12 +29,24 @@ class Company(NestedSet):
@frappe.whitelist()
def check_if_transactions_exist(self):
exists = False
for doctype in ["Sales Invoice", "Delivery Note", "Sales Order", "Quotation",
"Purchase Invoice", "Purchase Receipt", "Purchase Order", "Supplier Quotation"]:
if frappe.db.sql("""select name from `tab%s` where company=%s and docstatus=1
limit 1""" % (doctype, "%s"), self.name):
exists = True
break
for doctype in [
"Sales Invoice",
"Delivery Note",
"Sales Order",
"Quotation",
"Purchase Invoice",
"Purchase Receipt",
"Purchase Order",
"Supplier Quotation",
]:
if frappe.db.sql(
"""select name from `tab%s` where company=%s and docstatus=1
limit 1"""
% (doctype, "%s"),
self.name,
):
exists = True
break
return exists
@@ -56,7 +68,7 @@ class Company(NestedSet):
def validate_abbr(self):
if not self.abbr:
self.abbr = ''.join(c[0] for c in self.company_name.split()).upper()
self.abbr = "".join(c[0] for c in self.company_name.split()).upper()
self.abbr = self.abbr.strip()
@@ -66,7 +78,9 @@ class Company(NestedSet):
if not self.abbr.strip():
frappe.throw(_("Abbreviation is mandatory"))
if frappe.db.sql("select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)):
if frappe.db.sql(
"select abbr from tabCompany where name!=%s and abbr=%s", (self.name, self.abbr)
):
frappe.throw(_("Abbreviation already used for another company"))
@frappe.whitelist()
@@ -75,37 +89,57 @@ class Company(NestedSet):
def validate_default_accounts(self):
accounts = [
["Default Bank Account", "default_bank_account"], ["Default Cash Account", "default_cash_account"],
["Default Receivable Account", "default_receivable_account"], ["Default Payable Account", "default_payable_account"],
["Default Expense Account", "default_expense_account"], ["Default Income Account", "default_income_account"],
["Stock Received But Not Billed Account", "stock_received_but_not_billed"], ["Stock Adjustment Account", "stock_adjustment_account"],
["Expense Included In Valuation Account", "expenses_included_in_valuation"], ["Default Payroll Payable Account", "default_payroll_payable_account"]
["Default Bank Account", "default_bank_account"],
["Default Cash Account", "default_cash_account"],
["Default Receivable Account", "default_receivable_account"],
["Default Payable Account", "default_payable_account"],
["Default Expense Account", "default_expense_account"],
["Default Income Account", "default_income_account"],
["Stock Received But Not Billed Account", "stock_received_but_not_billed"],
["Stock Adjustment Account", "stock_adjustment_account"],
["Expense Included In Valuation Account", "expenses_included_in_valuation"],
["Default Payroll Payable Account", "default_payroll_payable_account"],
]
for account in accounts:
if self.get(account[1]):
for_company = frappe.db.get_value("Account", self.get(account[1]), "company")
if for_company != self.name:
frappe.throw(_("Account {0} does not belong to company: {1}").format(self.get(account[1]), self.name))
frappe.throw(
_("Account {0} does not belong to company: {1}").format(self.get(account[1]), self.name)
)
if get_account_currency(self.get(account[1])) != self.default_currency:
error_message = _("{0} currency must be same as company's default currency. Please select another account.") \
.format(frappe.bold(account[0]))
error_message = _(
"{0} currency must be same as company's default currency. Please select another account."
).format(frappe.bold(account[0]))
frappe.throw(error_message)
def validate_currency(self):
if self.is_new():
return
self.previous_default_currency = frappe.get_cached_value('Company', self.name, "default_currency")
if self.default_currency and self.previous_default_currency and \
self.default_currency != self.previous_default_currency and \
self.check_if_transactions_exist():
frappe.throw(_("Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."))
self.previous_default_currency = frappe.get_cached_value(
"Company", self.name, "default_currency"
)
if (
self.default_currency
and self.previous_default_currency
and self.default_currency != self.previous_default_currency
and self.check_if_transactions_exist()
):
frappe.throw(
_(
"Cannot change company's default currency, because there are existing transactions. Transactions must be cancelled to change the default currency."
)
)
def on_update(self):
NestedSet.on_update(self)
if not frappe.db.sql("""select name from tabAccount
where company=%s and docstatus<2 limit 1""", self.name):
if not frappe.db.sql(
"""select name from tabAccount
where company=%s and docstatus<2 limit 1""",
self.name,
):
if not frappe.local.flags.ignore_chart_of_accounts:
frappe.flags.country_change = True
self.create_default_accounts()
@@ -120,7 +154,8 @@ class Company(NestedSet):
if not frappe.db.get_value("Department", {"company": self.name}):
from erpnext.setup.setup_wizard.operations.install_fixtures import install_post_company_fixtures
install_post_company_fixtures(frappe._dict({'company_name': self.name}))
install_post_company_fixtures(frappe._dict({"company_name": self.name}))
if not frappe.local.flags.ignore_chart_of_accounts:
self.set_default_accounts()
@@ -130,12 +165,15 @@ class Company(NestedSet):
if self.default_currency:
frappe.db.set_value("Currency", self.default_currency, "enabled", 1)
if hasattr(frappe.local, 'enable_perpetual_inventory') and \
self.name in frappe.local.enable_perpetual_inventory:
if (
hasattr(frappe.local, "enable_perpetual_inventory")
and self.name in frappe.local.enable_perpetual_inventory
):
frappe.local.enable_perpetual_inventory[self.name] = self.enable_perpetual_inventory
if frappe.flags.parent_company_changed:
from frappe.utils.nestedset import rebuild_tree
rebuild_tree("Company", "parent_company")
frappe.clear_cache()
@@ -146,31 +184,48 @@ class Company(NestedSet):
{"warehouse_name": _("Stores"), "is_group": 0},
{"warehouse_name": _("Work In Progress"), "is_group": 0},
{"warehouse_name": _("Finished Goods"), "is_group": 0},
{"warehouse_name": _("Goods In Transit"), "is_group": 0, "warehouse_type": "Transit"}]:
{"warehouse_name": _("Goods In Transit"), "is_group": 0, "warehouse_type": "Transit"},
]:
if not frappe.db.exists("Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)):
warehouse = frappe.get_doc({
"doctype":"Warehouse",
"warehouse_name": wh_detail["warehouse_name"],
"is_group": wh_detail["is_group"],
"company": self.name,
"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr) \
if not wh_detail["is_group"] else "",
"warehouse_type" : wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None
})
if not frappe.db.exists(
"Warehouse", "{0} - {1}".format(wh_detail["warehouse_name"], self.abbr)
):
warehouse = frappe.get_doc(
{
"doctype": "Warehouse",
"warehouse_name": wh_detail["warehouse_name"],
"is_group": wh_detail["is_group"],
"company": self.name,
"parent_warehouse": "{0} - {1}".format(_("All Warehouses"), self.abbr)
if not wh_detail["is_group"]
else "",
"warehouse_type": wh_detail["warehouse_type"] if "warehouse_type" in wh_detail else None,
}
)
warehouse.flags.ignore_permissions = True
warehouse.flags.ignore_mandatory = True
warehouse.insert()
def create_default_accounts(self):
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import create_charts
frappe.local.flags.ignore_root_company_validation = True
create_charts(self.name, self.chart_of_accounts, self.existing_company)
frappe.db.set(self, "default_receivable_account", frappe.db.get_value("Account",
{"company": self.name, "account_type": "Receivable", "is_group": 0}))
frappe.db.set(self, "default_payable_account", frappe.db.get_value("Account",
{"company": self.name, "account_type": "Payable", "is_group": 0}))
frappe.db.set(
self,
"default_receivable_account",
frappe.db.get_value(
"Account", {"company": self.name, "account_type": "Receivable", "is_group": 0}
),
)
frappe.db.set(
self,
"default_payable_account",
frappe.db.get_value(
"Account", {"company": self.name, "account_type": "Payable", "is_group": 0}
),
)
def validate_coa_input(self):
if self.create_chart_of_accounts_based_on == "Existing Company":
@@ -187,34 +242,46 @@ class Company(NestedSet):
def validate_perpetual_inventory(self):
if not self.get("__islocal"):
if cint(self.enable_perpetual_inventory) == 1 and not self.default_inventory_account:
frappe.msgprint(_("Set default inventory account for perpetual inventory"),
alert=True, indicator='orange')
frappe.msgprint(
_("Set default inventory account for perpetual inventory"), alert=True, indicator="orange"
)
def validate_provisional_account_for_non_stock_items(self):
if not self.get("__islocal"):
if cint(self.enable_provisional_accounting_for_non_stock_items) == 1 and not self.default_provisional_account:
frappe.throw(_("Set default {0} account for non stock items").format(
frappe.bold('Provisional Account')))
if (
cint(self.enable_provisional_accounting_for_non_stock_items) == 1
and not self.default_provisional_account
):
frappe.throw(
_("Set default {0} account for non stock items").format(frappe.bold("Provisional Account"))
)
make_property_setter("Purchase Receipt", "provisional_expense_account", "hidden",
not self.enable_provisional_accounting_for_non_stock_items, "Check", validate_fields_for_doctype=False)
make_property_setter(
"Purchase Receipt",
"provisional_expense_account",
"hidden",
not self.enable_provisional_accounting_for_non_stock_items,
"Check",
validate_fields_for_doctype=False,
)
def check_country_change(self):
frappe.flags.country_change = False
if not self.is_new() and \
self.country != frappe.get_cached_value('Company', self.name, 'country'):
if not self.is_new() and self.country != frappe.get_cached_value(
"Company", self.name, "country"
):
frappe.flags.country_change = True
def set_chart_of_accounts(self):
''' If parent company is set, chart of accounts will be based on that company '''
"""If parent company is set, chart of accounts will be based on that company"""
if self.parent_company:
self.create_chart_of_accounts_based_on = "Existing Company"
self.existing_company = self.parent_company
def validate_parent_company(self):
if self.parent_company:
is_group = frappe.get_value('Company', self.parent_company, 'is_group')
is_group = frappe.get_value("Company", self.parent_company, "is_group")
if not is_group:
frappe.throw(_("Parent Company must be a group company"))
@@ -228,29 +295,33 @@ class Company(NestedSet):
"depreciation_expense_account": "Depreciation",
"capital_work_in_progress_account": "Capital Work in Progress",
"asset_received_but_not_billed": "Asset Received But Not Billed",
"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation"
"expenses_included_in_asset_valuation": "Expenses Included In Asset Valuation",
}
if self.enable_perpetual_inventory:
default_accounts.update({
"stock_received_but_not_billed": "Stock Received But Not Billed",
"default_inventory_account": "Stock",
"stock_adjustment_account": "Stock Adjustment",
"expenses_included_in_valuation": "Expenses Included In Valuation",
"default_expense_account": "Cost of Goods Sold"
})
default_accounts.update(
{
"stock_received_but_not_billed": "Stock Received But Not Billed",
"default_inventory_account": "Stock",
"stock_adjustment_account": "Stock Adjustment",
"expenses_included_in_valuation": "Expenses Included In Valuation",
"default_expense_account": "Cost of Goods Sold",
}
)
if self.update_default_account:
for default_account in default_accounts:
self._set_default_account(default_account, default_accounts.get(default_account))
if not self.default_income_account:
income_account = frappe.db.get_value("Account",
{"account_name": _("Sales"), "company": self.name, "is_group": 0})
income_account = frappe.db.get_value(
"Account", {"account_name": _("Sales"), "company": self.name, "is_group": 0}
)
if not income_account:
income_account = frappe.db.get_value("Account",
{"account_name": _("Sales Account"), "company": self.name})
income_account = frappe.db.get_value(
"Account", {"account_name": _("Sales Account"), "company": self.name}
)
self.db_set("default_income_account", income_account)
@@ -258,32 +329,38 @@ class Company(NestedSet):
self.db_set("default_payable_account", self.default_payable_account)
if not self.default_payroll_payable_account:
payroll_payable_account = frappe.db.get_value("Account",
{"account_name": _("Payroll Payable"), "company": self.name, "is_group": 0})
payroll_payable_account = frappe.db.get_value(
"Account", {"account_name": _("Payroll Payable"), "company": self.name, "is_group": 0}
)
self.db_set("default_payroll_payable_account", payroll_payable_account)
if not self.default_employee_advance_account:
employe_advance_account = frappe.db.get_value("Account",
{"account_name": _("Employee Advances"), "company": self.name, "is_group": 0})
employe_advance_account = frappe.db.get_value(
"Account", {"account_name": _("Employee Advances"), "company": self.name, "is_group": 0}
)
self.db_set("default_employee_advance_account", employe_advance_account)
if not self.write_off_account:
write_off_acct = frappe.db.get_value("Account",
{"account_name": _("Write Off"), "company": self.name, "is_group": 0})
write_off_acct = frappe.db.get_value(
"Account", {"account_name": _("Write Off"), "company": self.name, "is_group": 0}
)
self.db_set("write_off_account", write_off_acct)
if not self.exchange_gain_loss_account:
exchange_gain_loss_acct = frappe.db.get_value("Account",
{"account_name": _("Exchange Gain/Loss"), "company": self.name, "is_group": 0})
exchange_gain_loss_acct = frappe.db.get_value(
"Account", {"account_name": _("Exchange Gain/Loss"), "company": self.name, "is_group": 0}
)
self.db_set("exchange_gain_loss_account", exchange_gain_loss_acct)
if not self.disposal_account:
disposal_acct = frappe.db.get_value("Account",
{"account_name": _("Gain/Loss on Asset Disposal"), "company": self.name, "is_group": 0})
disposal_acct = frappe.db.get_value(
"Account",
{"account_name": _("Gain/Loss on Asset Disposal"), "company": self.name, "is_group": 0},
)
self.db_set("disposal_account", disposal_acct)
@@ -291,35 +368,39 @@ class Company(NestedSet):
if self.get(fieldname):
return
account = frappe.db.get_value("Account", {"account_type": account_type, "is_group": 0, "company": self.name})
account = frappe.db.get_value(
"Account", {"account_type": account_type, "is_group": 0, "company": self.name}
)
if account:
self.db_set(fieldname, account)
def set_mode_of_payment_account(self):
cash = frappe.db.get_value('Mode of Payment', {'type': 'Cash'}, 'name')
if cash and self.default_cash_account \
and not frappe.db.get_value('Mode of Payment Account', {'company': self.name, 'parent': cash}):
mode_of_payment = frappe.get_doc('Mode of Payment', cash, for_update=True)
mode_of_payment.append('accounts', {
'company': self.name,
'default_account': self.default_cash_account
})
cash = frappe.db.get_value("Mode of Payment", {"type": "Cash"}, "name")
if (
cash
and self.default_cash_account
and not frappe.db.get_value("Mode of Payment Account", {"company": self.name, "parent": cash})
):
mode_of_payment = frappe.get_doc("Mode of Payment", cash, for_update=True)
mode_of_payment.append(
"accounts", {"company": self.name, "default_account": self.default_cash_account}
)
mode_of_payment.save(ignore_permissions=True)
def create_default_cost_center(self):
cc_list = [
{
'cost_center_name': self.name,
'company':self.name,
'is_group': 1,
'parent_cost_center':None
"cost_center_name": self.name,
"company": self.name,
"is_group": 1,
"parent_cost_center": None,
},
{
'cost_center_name':_('Main'),
'company':self.name,
'is_group':0,
'parent_cost_center':self.name + ' - ' + self.abbr
"cost_center_name": _("Main"),
"company": self.name,
"is_group": 0,
"parent_cost_center": self.name + " - " + self.abbr,
},
]
for cc in cc_list:
@@ -338,26 +419,32 @@ class Company(NestedSet):
def after_rename(self, olddn, newdn, merge=False):
frappe.db.set(self, "company_name", newdn)
frappe.db.sql("""update `tabDefaultValue` set defvalue=%s
where defkey='Company' and defvalue=%s""", (newdn, olddn))
frappe.db.sql(
"""update `tabDefaultValue` set defvalue=%s
where defkey='Company' and defvalue=%s""",
(newdn, olddn),
)
clear_defaults_cache()
def abbreviate(self):
self.abbr = ''.join(c[0].upper() for c in self.company_name.split())
self.abbr = "".join(c[0].upper() for c in self.company_name.split())
def on_trash(self):
"""
Trash accounts and cost centers for this company if no gl entry exists
Trash accounts and cost centers for this company if no gl entry exists
"""
NestedSet.validate_if_child_exists(self)
frappe.utils.nestedset.update_nsm(self)
rec = frappe.db.sql("SELECT name from `tabGL Entry` where company = %s", self.name)
if not rec:
frappe.db.sql("""delete from `tabBudget Account`
frappe.db.sql(
"""delete from `tabBudget Account`
where exists(select name from tabBudget
where name=`tabBudget Account`.parent and company = %s)""", self.name)
where name=`tabBudget Account`.parent and company = %s)""",
self.name,
)
for doctype in ["Account", "Cost Center", "Budget", "Party Account"]:
frappe.db.sql("delete from `tab{0}` where company = %s".format(doctype), self.name)
@@ -372,26 +459,37 @@ class Company(NestedSet):
# clear default accounts, warehouses from item
warehouses = frappe.db.sql_list("select name from tabWarehouse where company=%s", self.name)
if warehouses:
frappe.db.sql("""delete from `tabItem Reorder` where warehouse in (%s)"""
% ', '.join(['%s']*len(warehouses)), tuple(warehouses))
frappe.db.sql(
"""delete from `tabItem Reorder` where warehouse in (%s)"""
% ", ".join(["%s"] * len(warehouses)),
tuple(warehouses),
)
# reset default company
frappe.db.sql("""update `tabSingles` set value=""
frappe.db.sql(
"""update `tabSingles` set value=""
where doctype='Global Defaults' and field='default_company'
and value=%s""", self.name)
and value=%s""",
self.name,
)
# reset default company
frappe.db.sql("""update `tabSingles` set value=""
frappe.db.sql(
"""update `tabSingles` set value=""
where doctype='Chart of Accounts Importer' and field='company'
and value=%s""", self.name)
and value=%s""",
self.name,
)
# delete BOMs
boms = frappe.db.sql_list("select name from tabBOM where company=%s", self.name)
if boms:
frappe.db.sql("delete from tabBOM where company=%s", self.name)
for dt in ("BOM Operation", "BOM Item", "BOM Scrap Item", "BOM Explosion Item"):
frappe.db.sql("delete from `tab%s` where parent in (%s)"""
% (dt, ', '.join(['%s']*len(boms))), tuple(boms))
frappe.db.sql(
"delete from `tab%s` where parent in (%s)" "" % (dt, ", ".join(["%s"] * len(boms))),
tuple(boms),
)
frappe.db.sql("delete from tabEmployee where company=%s", self.name)
frappe.db.sql("delete from tabDepartment where company=%s", self.name)
@@ -404,18 +502,20 @@ class Company(NestedSet):
frappe.db.sql("delete from `tabItem Tax Template` where company=%s", self.name)
# delete Process Deferred Accounts if no GL Entry found
if not frappe.db.get_value('GL Entry', {'company': self.name}):
if not frappe.db.get_value("GL Entry", {"company": self.name}):
frappe.db.sql("delete from `tabProcess Deferred Accounting` where company=%s", self.name)
def check_parent_changed(self):
frappe.flags.parent_company_changed = False
if not self.is_new() and \
self.parent_company != frappe.db.get_value("Company", self.name, "parent_company"):
if not self.is_new() and self.parent_company != frappe.db.get_value(
"Company", self.name, "parent_company"
):
frappe.flags.parent_company_changed = True
def get_name_with_abbr(name, company):
company_abbr = frappe.get_cached_value('Company', company, "abbr")
company_abbr = frappe.get_cached_value("Company", company, "abbr")
parts = name.split(" - ")
if parts[-1].lower() != company_abbr.lower():
@@ -423,21 +523,27 @@ def get_name_with_abbr(name, company):
return " - ".join(parts)
def install_country_fixtures(company, country):
path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
path = frappe.get_app_path("erpnext", "regional", frappe.scrub(country))
if os.path.exists(path.encode("utf-8")):
try:
module_name = "erpnext.regional.{0}.setup.setup".format(frappe.scrub(country))
frappe.get_attr(module_name)(company, False)
except Exception as e:
frappe.log_error()
frappe.throw(_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(frappe.bold(country)))
frappe.throw(
_("Failed to setup defaults for country {0}. Please contact support@erpnext.com").format(
frappe.bold(country)
)
)
def update_company_current_month_sales(company):
current_month_year = formatdate(today(), "MM-yyyy")
results = frappe.db.sql('''
results = frappe.db.sql(
"""
SELECT
SUM(base_grand_total) AS total,
DATE_FORMAT(`posting_date`, '%m-%Y') AS month_year
@@ -449,44 +555,58 @@ def update_company_current_month_sales(company):
AND company = {company}
GROUP BY
month_year
'''.format(current_month_year=current_month_year, company=frappe.db.escape(company)),
as_dict = True)
""".format(
current_month_year=current_month_year, company=frappe.db.escape(company)
),
as_dict=True,
)
monthly_total = results[0]['total'] if len(results) > 0 else 0
monthly_total = results[0]["total"] if len(results) > 0 else 0
frappe.db.set_value("Company", company, "total_monthly_sales", monthly_total)
def update_company_monthly_sales(company):
'''Cache past year monthly sales of every company based on sales invoices'''
"""Cache past year monthly sales of every company based on sales invoices"""
import json
from frappe.utils.goal import get_monthly_results
filter_str = "company = {0} and status != 'Draft' and docstatus=1".format(frappe.db.escape(company))
month_to_value_dict = get_monthly_results("Sales Invoice", "base_grand_total",
"posting_date", filter_str, "sum")
filter_str = "company = {0} and status != 'Draft' and docstatus=1".format(
frappe.db.escape(company)
)
month_to_value_dict = get_monthly_results(
"Sales Invoice", "base_grand_total", "posting_date", filter_str, "sum"
)
frappe.db.set_value("Company", company, "sales_monthly_history", json.dumps(month_to_value_dict))
def update_transactions_annual_history(company, commit=False):
transactions_history = get_all_transactions_annual_history(company)
frappe.db.set_value("Company", company, "transactions_annual_history", json.dumps(transactions_history))
frappe.db.set_value(
"Company", company, "transactions_annual_history", json.dumps(transactions_history)
)
if commit:
frappe.db.commit()
def cache_companies_monthly_sales_history():
companies = [d['name'] for d in frappe.get_list("Company")]
companies = [d["name"] for d in frappe.get_list("Company")]
for company in companies:
update_company_monthly_sales(company)
update_transactions_annual_history(company)
frappe.db.commit()
@frappe.whitelist()
def get_children(doctype, parent=None, company=None, is_root=False):
if parent == None or parent == "All Companies":
parent = ""
return frappe.db.sql("""
return frappe.db.sql(
"""
select
name as value,
is_group as expandable
@@ -495,25 +615,30 @@ def get_children(doctype, parent=None, company=None, is_root=False):
where
ifnull(parent_company, "")={parent}
""".format(
doctype = doctype,
parent=frappe.db.escape(parent)
), as_dict=1)
doctype=doctype, parent=frappe.db.escape(parent)
),
as_dict=1,
)
@frappe.whitelist()
def add_node():
from frappe.desk.treeview import make_tree_args
args = frappe.form_dict
args = make_tree_args(**args)
if args.parent_company == 'All Companies':
if args.parent_company == "All Companies":
args.parent_company = None
frappe.get_doc(args).insert()
def get_all_transactions_annual_history(company):
out = {}
items = frappe.db.sql('''
items = frappe.db.sql(
"""
select transaction_date, count(*) as count
from (
@@ -553,61 +678,68 @@ def get_all_transactions_annual_history(company):
group by
transaction_date
''', (company), as_dict=True)
""",
(company),
as_dict=True,
)
for d in items:
timestamp = get_timestamp(d["transaction_date"])
out.update({ timestamp: d["count"] })
out.update({timestamp: d["count"]})
return out
def get_timeline_data(doctype, name):
'''returns timeline data based on linked records in dashboard'''
"""returns timeline data based on linked records in dashboard"""
out = {}
date_to_value_dict = {}
history = frappe.get_cached_value('Company', name, "transactions_annual_history")
history = frappe.get_cached_value("Company", name, "transactions_annual_history")
try:
date_to_value_dict = json.loads(history) if history and '{' in history else None
date_to_value_dict = json.loads(history) if history and "{" in history else None
except ValueError:
date_to_value_dict = None
if date_to_value_dict is None:
update_transactions_annual_history(name, True)
history = frappe.get_cached_value('Company', name, "transactions_annual_history")
return json.loads(history) if history and '{' in history else {}
history = frappe.get_cached_value("Company", name, "transactions_annual_history")
return json.loads(history) if history and "{" in history else {}
return date_to_value_dict
@frappe.whitelist()
def get_default_company_address(name, sort_key='is_primary_address', existing_address=None):
if sort_key not in ['is_shipping_address', 'is_primary_address']:
def get_default_company_address(name, sort_key="is_primary_address", existing_address=None):
if sort_key not in ["is_shipping_address", "is_primary_address"]:
return None
out = frappe.db.sql(""" SELECT
out = frappe.db.sql(
""" SELECT
addr.name, addr.%s
FROM
`tabAddress` addr, `tabDynamic Link` dl
WHERE
dl.parent = addr.name and dl.link_doctype = 'Company' and
dl.link_name = %s and ifnull(addr.disabled, 0) = 0
""" %(sort_key, '%s'), (name)) #nosec
"""
% (sort_key, "%s"),
(name),
) # nosec
if existing_address:
if existing_address in [d[0] for d in out]:
return existing_address
if out:
return sorted(out, key = functools.cmp_to_key(lambda x,y: cmp(y[1], x[1])))[0][0]
return sorted(out, key=functools.cmp_to_key(lambda x, y: cmp(y[1], x[1])))[0][0]
else:
return None
@frappe.whitelist()
def create_transaction_deletion_request(company):
tdr = frappe.get_doc({
'doctype': 'Transaction Deletion Record',
'company': company
})
tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
tdr.insert()
tdr.submit()

View File

@@ -1,41 +1,27 @@
from frappe import _
def get_data():
return {
'graph': True,
'graph_method': "frappe.utils.goal.get_monthly_goal_graph_data",
'graph_method_args': {
'title': _('Sales'),
'goal_value_field': 'monthly_sales_target',
'goal_total_field': 'total_monthly_sales',
'goal_history_field': 'sales_monthly_history',
'goal_doctype': 'Sales Invoice',
'goal_doctype_link': 'company',
'goal_field': 'base_grand_total',
'date_field': 'posting_date',
'filter_str': "docstatus = 1 and is_opening != 'Yes'",
'aggregation': 'sum'
"graph": True,
"graph_method": "frappe.utils.goal.get_monthly_goal_graph_data",
"graph_method_args": {
"title": _("Sales"),
"goal_value_field": "monthly_sales_target",
"goal_total_field": "total_monthly_sales",
"goal_history_field": "sales_monthly_history",
"goal_doctype": "Sales Invoice",
"goal_doctype_link": "company",
"goal_field": "base_grand_total",
"date_field": "posting_date",
"filter_str": "docstatus = 1 and is_opening != 'Yes'",
"aggregation": "sum",
},
'fieldname': 'company',
'transactions': [
{
'label': _('Pre Sales'),
'items': ['Quotation']
},
{
'label': _('Orders'),
'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
},
{
'label': _('Support'),
'items': ['Issue']
},
{
'label': _('Projects'),
'items': ['Project']
}
]
"fieldname": "company",
"transactions": [
{"label": _("Pre Sales"), "items": ["Quotation"]},
{"label": _("Orders"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
{"label": _("Support"), "items": ["Issue"]},
{"label": _("Projects"), "items": ["Project"]},
],
}

View File

@@ -14,7 +14,8 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
test_ignore = ["Account", "Cost Center", "Payment Terms Template", "Salary Component", "Warehouse"]
test_dependencies = ["Fiscal Year"]
test_records = frappe.get_test_records('Company')
test_records = frappe.get_test_records("Company")
class TestCompany(unittest.TestCase):
def test_coa_based_on_existing_company(self):
@@ -37,8 +38,8 @@ class TestCompany(unittest.TestCase):
"account_type": "Cash",
"is_group": 0,
"root_type": "Asset",
"parent_account": "Cash In Hand - CFEC"
}
"parent_account": "Cash In Hand - CFEC",
},
}
for account, acc_property in expected_results.items():
@@ -69,15 +70,22 @@ class TestCompany(unittest.TestCase):
company.chart_of_accounts = template
company.save()
account_types = ["Cost of Goods Sold", "Depreciation",
"Expenses Included In Valuation", "Fixed Asset", "Payable", "Receivable",
"Stock Adjustment", "Stock Received But Not Billed", "Bank", "Cash", "Stock"]
account_types = [
"Cost of Goods Sold",
"Depreciation",
"Expenses Included In Valuation",
"Fixed Asset",
"Payable",
"Receivable",
"Stock Adjustment",
"Stock Received But Not Billed",
"Bank",
"Cash",
"Stock",
]
for account_type in account_types:
filters = {
"company": template,
"account_type": account_type
}
filters = {"company": template, "account_type": account_type}
if account_type in ["Bank", "Cash"]:
filters["is_group"] = 1
@@ -90,8 +98,11 @@ class TestCompany(unittest.TestCase):
frappe.delete_doc("Company", template)
def delete_mode_of_payment(self, company):
frappe.db.sql(""" delete from `tabMode of Payment Account`
where company =%s """, (company))
frappe.db.sql(
""" delete from `tabMode of Payment Account`
where company =%s """,
(company),
)
def test_basic_tree(self, records=None):
min_lft = 1
@@ -101,12 +112,12 @@ class TestCompany(unittest.TestCase):
records = test_records[2:]
for company in records:
lft, rgt, parent_company = frappe.db.get_value("Company", company["company_name"],
["lft", "rgt", "parent_company"])
lft, rgt, parent_company = frappe.db.get_value(
"Company", company["company_name"], ["lft", "rgt", "parent_company"]
)
if parent_company:
parent_lft, parent_rgt = frappe.db.get_value("Company", parent_company,
["lft", "rgt"])
parent_lft, parent_rgt = frappe.db.get_value("Company", parent_company, ["lft", "rgt"])
else:
# root
parent_lft = min_lft - 1
@@ -125,8 +136,11 @@ class TestCompany(unittest.TestCase):
def get_no_of_children(companies, no_of_children):
children = []
for company in companies:
children += frappe.db.sql_list("""select name from `tabCompany`
where ifnull(parent_company, '')=%s""", company or '')
children += frappe.db.sql_list(
"""select name from `tabCompany`
where ifnull(parent_company, '')=%s""",
company or "",
)
if len(children):
return get_no_of_children(children, no_of_children + len(children))
@@ -148,40 +162,45 @@ class TestCompany(unittest.TestCase):
child_company.save()
self.test_basic_tree()
def create_company_communication(doctype, docname):
comm = frappe.get_doc({
comm = frappe.get_doc(
{
"doctype": "Communication",
"communication_type": "Communication",
"content": "Deduplication of Links",
"communication_medium": "Email",
"reference_doctype":doctype,
"reference_name":docname
})
"reference_doctype": doctype,
"reference_name": docname,
}
)
comm.insert()
def create_child_company():
child_company = frappe.db.exists("Company", "Test Company")
if not child_company:
child_company = frappe.get_doc({
"doctype":"Company",
"company_name":"Test Company",
"abbr":"test_company",
"default_currency":"INR"
})
child_company = frappe.get_doc(
{
"doctype": "Company",
"company_name": "Test Company",
"abbr": "test_company",
"default_currency": "INR",
}
)
child_company.insert()
else:
child_company = frappe.get_doc("Company", child_company)
return child_company.name
def create_test_lead_in_company(company):
lead = frappe.db.exists("Lead", "Test Lead in new company")
if not lead:
lead = frappe.get_doc({
"doctype": "Lead",
"lead_name": "Test Lead in new company",
"scompany": company
})
lead = frappe.get_doc(
{"doctype": "Lead", "lead_name": "Test Lead in new company", "scompany": company}
)
lead.insert()
else:
lead = frappe.get_doc("Lead", lead)

View File

@@ -17,13 +17,17 @@ class CurrencyExchange(Document):
# If both selling and buying enabled
purpose = "Selling-Buying"
if cint(self.for_buying)==0 and cint(self.for_selling)==1:
if cint(self.for_buying) == 0 and cint(self.for_selling) == 1:
purpose = "Selling"
if cint(self.for_buying)==1 and cint(self.for_selling)==0:
if cint(self.for_buying) == 1 and cint(self.for_selling) == 0:
purpose = "Buying"
self.name = '{0}-{1}-{2}{3}'.format(formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
self.from_currency, self.to_currency, ("-" + purpose) if purpose else "")
self.name = "{0}-{1}-{2}{3}".format(
formatdate(get_datetime_str(self.date), "yyyy-MM-dd"),
self.from_currency,
self.to_currency,
("-" + purpose) if purpose else "",
)
def validate(self):
self.validate_value("exchange_rate", ">", 0)

View File

@@ -8,7 +8,7 @@ from frappe.utils import cint, flt
from erpnext.setup.utils import get_exchange_rate
test_records = frappe.get_test_records('Currency Exchange')
test_records = frappe.get_test_records("Currency Exchange")
def save_new_records(test_records):
@@ -16,13 +16,19 @@ def save_new_records(test_records):
# If both selling and buying enabled
purpose = "Selling-Buying"
if cint(record.get("for_buying"))==0 and cint(record.get("for_selling"))==1:
if cint(record.get("for_buying")) == 0 and cint(record.get("for_selling")) == 1:
purpose = "Selling"
if cint(record.get("for_buying"))==1 and cint(record.get("for_selling"))==0:
if cint(record.get("for_buying")) == 1 and cint(record.get("for_selling")) == 0:
purpose = "Buying"
kwargs = dict(
doctype=record.get("doctype"),
docname=record.get("date") + '-' + record.get("from_currency") + '-' + record.get("to_currency") + '-' + purpose,
docname=record.get("date")
+ "-"
+ record.get("from_currency")
+ "-"
+ record.get("to_currency")
+ "-"
+ purpose,
fieldname="exchange_rate",
value=record.get("exchange_rate"),
)

View File

@@ -8,7 +8,8 @@ from frappe.utils.nestedset import NestedSet, get_root_of
class CustomerGroup(NestedSet):
nsm_parent_field = 'parent_customer_group'
nsm_parent_field = "parent_customer_group"
def validate(self):
if not self.parent_customer_group:
self.parent_customer_group = get_root_of("Customer Group")
@@ -22,12 +23,18 @@ class CustomerGroup(NestedSet):
if frappe.db.exists("Customer", self.name):
frappe.msgprint(_("A customer with the same name already exists"), raise_exception=1)
def get_parent_customer_groups(customer_group):
lft, rgt = frappe.db.get_value("Customer Group", customer_group, ['lft', 'rgt'])
return frappe.db.sql("""select name from `tabCustomer Group`
def get_parent_customer_groups(customer_group):
lft, rgt = frappe.db.get_value("Customer Group", customer_group, ["lft", "rgt"])
return frappe.db.sql(
"""select name from `tabCustomer Group`
where lft <= %s and rgt >= %s
order by lft asc""", (lft, rgt), as_dict=True)
order by lft asc""",
(lft, rgt),
as_dict=True,
)
def on_doctype_update():
frappe.db.add_index("Customer Group", ["lft", "rgt"])

View File

@@ -4,7 +4,6 @@
test_ignore = ["Price List"]
import frappe
test_records = frappe.get_test_records('Customer Group')
test_records = frappe.get_test_records("Customer Group")

File diff suppressed because it is too large Load Diff

View File

@@ -1,34 +1,65 @@
import random
def get_random_quote():
quotes = [
("Start by doing what's necessary; then do what's possible; and suddenly you are doing the impossible.", "Francis of Assisi"),
("The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.", "Hellen Keller"),
("I can't change the direction of the wind, but I can adjust my sails to always reach my destination.", "Jimmy Dean"),
(
"Start by doing what's necessary; then do what's possible; and suddenly you are doing the impossible.",
"Francis of Assisi",
),
(
"The best and most beautiful things in the world cannot be seen or even touched - they must be felt with the heart.",
"Hellen Keller",
),
(
"I can't change the direction of the wind, but I can adjust my sails to always reach my destination.",
"Jimmy Dean",
),
("We know what we are, but know not what we may be.", "William Shakespeare"),
("There are only two mistakes one can make along the road to truth; not going all the way, and not starting.", "Buddha"),
(
"There are only two mistakes one can make along the road to truth; not going all the way, and not starting.",
"Buddha",
),
("Always remember that you are absolutely unique. Just like everyone else.", "Margaret Mead"),
("You have to learn the rules of the game. And then you have to play better than anyone else.", "Albert Einstein"),
(
"You have to learn the rules of the game. And then you have to play better than anyone else.",
"Albert Einstein",
),
("Once we accept our limits, we go beyond them.", "Albert Einstein"),
("Quality is not an act, it is a habit.", "Aristotle"),
("The more that you read, the more things you will know. The more that you learn, the more places you'll go.", "Dr. Seuss"),
(
"The more that you read, the more things you will know. The more that you learn, the more places you'll go.",
"Dr. Seuss",
),
("From there to here, and here to there, funny things are everywhere.", "Dr. Seuss"),
("The secret of getting ahead is getting started.", "Mark Twain"),
("All generalizations are false, including this one.", "Mark Twain"),
("Don't let schooling interfere with your education.", "Mark Twain"),
("Cauliflower is nothing but cabbage with a college education.", "Mark Twain"),
("It's not the size of the dog in the fight, it's the size of the fight in the dog.", "Mark Twain"),
(
"It's not the size of the dog in the fight, it's the size of the fight in the dog.",
"Mark Twain",
),
("Climate is what we expect, weather is what we get.", "Mark Twain"),
("There are lies, damned lies and statistics.", "Mark Twain"),
("Happiness is when what you think, what you say, and what you do are in harmony.", "Mahatma Gandhi"),
("First they ignore you, then they laugh at you, then they fight you, then you win.", "Mahatma Gandhi"),
(
"Happiness is when what you think, what you say, and what you do are in harmony.",
"Mahatma Gandhi",
),
(
"First they ignore you, then they laugh at you, then they fight you, then you win.",
"Mahatma Gandhi",
),
("There is more to life than increasing its speed.", "Mahatma Gandhi"),
("A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.", "Mahatma Gandhi"),
(
"A small body of determined spirits fired by an unquenchable faith in their mission can alter the course of history.",
"Mahatma Gandhi",
),
("If two wrongs don't make a right, try three.", "Laurence J. Peter"),
("Inspiration exists, but it has to find you working.", "Pablo Picasso"),
("The worlds first speeding ticket was given to a man going 4 times the speed limit! Walter Arnold was traveling at a breakneck 8 miles an hour in a 2mph zone, and was caught by a policeman on bicycle and fined one shilling!"),
(
"The worlds first speeding ticket was given to a man going 4 times the speed limit! Walter Arnold was traveling at a breakneck 8 miles an hour in a 2mph zone, and was caught by a policeman on bicycle and fined one shilling!"
),
]
return random.choice(quotes)

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('Email Digest')
class TestEmailDigest(unittest.TestCase):
pass

View File

@@ -11,35 +11,37 @@ from frappe.utils import cint
keydict = {
# "key in defaults": "key in Global Defaults"
"fiscal_year": "current_fiscal_year",
'company': 'default_company',
'currency': 'default_currency',
"company": "default_company",
"currency": "default_currency",
"country": "country",
'hide_currency_symbol':'hide_currency_symbol',
'account_url':'account_url',
'disable_rounded_total': 'disable_rounded_total',
'disable_in_words': 'disable_in_words',
"hide_currency_symbol": "hide_currency_symbol",
"account_url": "account_url",
"disable_rounded_total": "disable_rounded_total",
"disable_in_words": "disable_in_words",
}
from frappe.model.document import Document
class GlobalDefaults(Document):
def on_update(self):
"""update defaults"""
for key in keydict:
frappe.db.set_default(key, self.get(keydict[key], ''))
frappe.db.set_default(key, self.get(keydict[key], ""))
# update year start date and year end date from fiscal_year
year_start_end_date = frappe.db.sql("""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""", self.current_fiscal_year)
year_start_end_date = frappe.db.sql(
"""select year_start_date, year_end_date
from `tabFiscal Year` where name=%s""",
self.current_fiscal_year,
)
if year_start_end_date:
ysd = year_start_end_date[0][0] or ''
yed = year_start_end_date[0][1] or ''
ysd = year_start_end_date[0][0] or ""
yed = year_start_end_date[0][1] or ""
if ysd and yed:
frappe.db.set_default('year_start_date', ysd.strftime('%Y-%m-%d'))
frappe.db.set_default('year_end_date', yed.strftime('%Y-%m-%d'))
frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d"))
frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d"))
# enable default currency
if self.default_currency:
@@ -59,21 +61,81 @@ class GlobalDefaults(Document):
self.disable_rounded_total = cint(self.disable_rounded_total)
# Make property setters to hide rounded total fields
for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
"Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"):
make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check", validate_fields_for_doctype=False)
for doctype in (
"Quotation",
"Sales Order",
"Sales Invoice",
"Delivery Note",
"Supplier Quotation",
"Purchase Order",
"Purchase Invoice",
"Purchase Receipt",
):
make_property_setter(
doctype,
"base_rounded_total",
"hidden",
self.disable_rounded_total,
"Check",
validate_fields_for_doctype=False,
)
make_property_setter(
doctype, "base_rounded_total", "print_hide", 1, "Check", validate_fields_for_doctype=False
)
make_property_setter(doctype, "rounded_total", "hidden", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, "rounded_total", "print_hide", self.disable_rounded_total, "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"rounded_total",
"hidden",
self.disable_rounded_total,
"Check",
validate_fields_for_doctype=False,
)
make_property_setter(
doctype,
"rounded_total",
"print_hide",
self.disable_rounded_total,
"Check",
validate_fields_for_doctype=False,
)
make_property_setter(doctype, "disable_rounded_total", "default", cint(self.disable_rounded_total), "Text", validate_fields_for_doctype=False)
make_property_setter(
doctype,
"disable_rounded_total",
"default",
cint(self.disable_rounded_total),
"Text",
validate_fields_for_doctype=False,
)
def toggle_in_words(self):
self.disable_in_words = cint(self.disable_in_words)
# Make property setters to hide in words fields
for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note",
"Supplier Quotation", "Purchase Order", "Purchase Invoice", "Purchase Receipt"):
make_property_setter(doctype, "in_words", "hidden", self.disable_in_words, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, "in_words", "print_hide", self.disable_in_words, "Check", validate_fields_for_doctype=False)
for doctype in (
"Quotation",
"Sales Order",
"Sales Invoice",
"Delivery Note",
"Supplier Quotation",
"Purchase Order",
"Purchase Invoice",
"Purchase Receipt",
):
make_property_setter(
doctype,
"in_words",
"hidden",
self.disable_in_words,
"Check",
validate_fields_for_doctype=False,
)
make_property_setter(
doctype,
"in_words",
"print_hide",
self.disable_in_words,
"Check",
validate_fields_for_doctype=False,
)

View File

@@ -16,12 +16,12 @@ from erpnext.e_commerce.product_data_engine.filters import ProductFiltersBuilder
class ItemGroup(NestedSet, WebsiteGenerator):
nsm_parent_field = 'parent_item_group'
nsm_parent_field = "parent_item_group"
website = frappe._dict(
condition_field = "show_in_website",
template = "templates/generators/item_group.html",
no_cache = 1,
no_breadcrumbs = 1
condition_field="show_in_website",
template="templates/generators/item_group.html",
no_cache=1,
no_breadcrumbs=1,
)
def autoname(self):
@@ -31,8 +31,8 @@ class ItemGroup(NestedSet, WebsiteGenerator):
super(ItemGroup, self).validate()
if not self.parent_item_group and not frappe.flags.in_test:
if frappe.db.exists("Item Group", _('All Item Groups')):
self.parent_item_group = _('All Item Groups')
if frappe.db.exists("Item Group", _("All Item Groups")):
self.parent_item_group = _("All Item Groups")
self.make_route()
self.validate_item_group_defaults()
@@ -44,15 +44,15 @@ class ItemGroup(NestedSet, WebsiteGenerator):
self.delete_child_item_groups_key()
def make_route(self):
'''Make website route'''
"""Make website route"""
if not self.route:
self.route = ''
self.route = ""
if self.parent_item_group:
parent_item_group = frappe.get_doc('Item Group', self.parent_item_group)
parent_item_group = frappe.get_doc("Item Group", self.parent_item_group)
# make parent route only if not root
if parent_item_group.parent_item_group and parent_item_group.route:
self.route = parent_item_group.route + '/'
self.route = parent_item_group.route + "/"
self.route += self.scrub(self.item_group_name)
@@ -66,28 +66,22 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def get_context(self, context):
context.show_search = True
context.body_class = "product-page"
context.page_length = cint(frappe.db.get_single_value('E Commerce Settings', 'products_per_page')) or 6
context.search_link = '/product_search'
context.page_length = (
cint(frappe.db.get_single_value("E Commerce Settings", "products_per_page")) or 6
)
context.search_link = "/product_search"
filter_engine = ProductFiltersBuilder(self.name)
context.field_filters = filter_engine.get_field_filters()
context.attribute_filters = filter_engine.get_attribute_filters()
context.update({
"parents": get_parent_item_groups(self.parent_item_group),
"title": self.name
})
context.update({"parents": get_parent_item_groups(self.parent_item_group), "title": self.name})
if self.slideshow:
values = {
'show_indicators': 1,
'show_controls': 0,
'rounded': 1,
'slider_name': self.slideshow
}
values = {"show_indicators": 1, "show_controls": 0, "rounded": 1, "slider_name": self.slideshow}
slideshow = frappe.get_doc("Website Slideshow", self.slideshow)
slides = slideshow.get({"doctype":"Website Slideshow Item"})
slides = slideshow.get({"doctype": "Website Slideshow Item"})
for index, slide in enumerate(slides):
values[f"slide_{index + 1}_image"] = slide.image
values[f"slide_{index + 1}_title"] = slide.heading
@@ -110,68 +104,64 @@ class ItemGroup(NestedSet, WebsiteGenerator):
def validate_item_group_defaults(self):
from erpnext.stock.doctype.item.item import validate_item_default_company_links
validate_item_default_company_links(self.item_group_defaults)
def get_child_groups_for_website(item_group_name, immediate=False, include_self=False):
"""Returns child item groups *excluding* passed group."""
item_group = frappe.get_cached_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
filters = {
"lft": [">", item_group.lft],
"rgt": ["<", item_group.rgt],
"show_in_website": 1
}
filters = {"lft": [">", item_group.lft], "rgt": ["<", item_group.rgt], "show_in_website": 1}
if immediate:
filters["parent_item_group"] = item_group_name
if include_self:
filters.update({
"lft": [">=", item_group.lft],
"rgt": ["<=", item_group.rgt]
})
filters.update({"lft": [">=", item_group.lft], "rgt": ["<=", item_group.rgt]})
return frappe.get_all("Item Group", filters=filters, fields=["name", "route"], order_by="name")
return frappe.get_all(
"Item Group",
filters=filters,
fields=["name", "route"],
order_by="name"
)
def get_child_item_groups(item_group_name):
item_group = frappe.get_cached_value("Item Group",
item_group_name, ["lft", "rgt"], as_dict=1)
item_group = frappe.get_cached_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
child_item_groups = [d.name for d in frappe.get_all('Item Group',
filters= {'lft': ('>=', item_group.lft),'rgt': ('<=', item_group.rgt)})]
child_item_groups = [
d.name
for d in frappe.get_all(
"Item Group", filters={"lft": (">=", item_group.lft), "rgt": ("<=", item_group.rgt)}
)
]
return child_item_groups or {}
def get_item_for_list_in_html(context):
# add missing absolute link in files
# user may forget it during upload
if (context.get("website_image") or "").startswith("files/"):
context["website_image"] = "/" + quote(context["website_image"])
context["show_availability_status"] = cint(frappe.db.get_single_value('E Commerce Settings',
'show_availability_status'))
context["show_availability_status"] = cint(
frappe.db.get_single_value("E Commerce Settings", "show_availability_status")
)
products_template = 'templates/includes/products_as_list.html'
products_template = "templates/includes/products_as_list.html"
return frappe.get_template(products_template).render(context)
def get_parent_item_groups(item_group_name, from_item=False):
base_nav_page = {"name": _("Shop by Category"), "route":"/shop-by-category"}
base_nav_page = {"name": _("Shop by Category"), "route": "/shop-by-category"}
if from_item and frappe.request.environ.get("HTTP_REFERER"):
# base page after 'Home' will vary on Item page
last_page = frappe.request.environ["HTTP_REFERER"].split('/')[-1]
last_page = frappe.request.environ["HTTP_REFERER"].split("/")[-1]
if last_page and last_page in ("shop-by-category", "all-products"):
base_nav_page_title = " ".join(last_page.split("-")).title()
base_nav_page = {"name": _(base_nav_page_title), "route":"/"+last_page}
base_nav_page = {"name": _(base_nav_page_title), "route": "/" + last_page}
base_parents = [
{"name": _("Home"), "route":"/"},
{"name": _("Home"), "route": "/"},
base_nav_page,
]
@@ -179,21 +169,27 @@ def get_parent_item_groups(item_group_name, from_item=False):
return base_parents
item_group = frappe.db.get_value("Item Group", item_group_name, ["lft", "rgt"], as_dict=1)
parent_groups = frappe.db.sql("""select name, route from `tabItem Group`
parent_groups = frappe.db.sql(
"""select name, route from `tabItem Group`
where lft <= %s and rgt >= %s
and show_in_website=1
order by lft asc""", (item_group.lft, item_group.rgt), as_dict=True)
order by lft asc""",
(item_group.lft, item_group.rgt),
as_dict=True,
)
return base_parents + parent_groups
def invalidate_cache_for(doc, item_group=None):
if not item_group:
item_group = doc.name
for d in get_parent_item_groups(item_group):
item_group_name = frappe.db.get_value("Item Group", d.get('name'))
item_group_name = frappe.db.get_value("Item Group", d.get("name"))
if item_group_name:
clear_cache(frappe.db.get_value('Item Group', item_group_name, 'route'))
clear_cache(frappe.db.get_value("Item Group", item_group_name, "route"))
def get_item_group_defaults(item, company):
item = frappe.get_cached_doc("Item", item)

View File

@@ -14,7 +14,8 @@ from frappe.utils.nestedset import (
rebuild_tree,
)
test_records = frappe.get_test_records('Item Group')
test_records = frappe.get_test_records("Item Group")
class TestItem(unittest.TestCase):
def test_basic_tree(self, records=None):
@@ -25,12 +26,12 @@ class TestItem(unittest.TestCase):
records = test_records[2:]
for item_group in records:
lft, rgt, parent_item_group = frappe.db.get_value("Item Group", item_group["item_group_name"],
["lft", "rgt", "parent_item_group"])
lft, rgt, parent_item_group = frappe.db.get_value(
"Item Group", item_group["item_group_name"], ["lft", "rgt", "parent_item_group"]
)
if parent_item_group:
parent_lft, parent_rgt = frappe.db.get_value("Item Group", parent_item_group,
["lft", "rgt"])
parent_lft, parent_rgt = frappe.db.get_value("Item Group", parent_item_group, ["lft", "rgt"])
else:
# root
parent_lft = min_lft - 1
@@ -55,8 +56,11 @@ class TestItem(unittest.TestCase):
def get_no_of_children(item_groups, no_of_children):
children = []
for ig in item_groups:
children += frappe.db.sql_list("""select name from `tabItem Group`
where ifnull(parent_item_group, '')=%s""", ig or '')
children += frappe.db.sql_list(
"""select name from `tabItem Group`
where ifnull(parent_item_group, '')=%s""",
ig or "",
)
if len(children):
return get_no_of_children(children, no_of_children + len(children))
@@ -119,7 +123,10 @@ class TestItem(unittest.TestCase):
def print_tree(self):
import json
print(json.dumps(frappe.db.sql("select name, lft, rgt from `tabItem Group` order by lft"), indent=1))
print(
json.dumps(frappe.db.sql("select name, lft, rgt from `tabItem Group` order by lft"), indent=1)
)
def test_move_leaf_into_another_group(self):
# before move
@@ -149,12 +156,20 @@ class TestItem(unittest.TestCase):
def test_delete_leaf(self):
# for checking later
parent_item_group = frappe.db.get_value("Item Group", "_Test Item Group B - 3", "parent_item_group")
parent_item_group = frappe.db.get_value(
"Item Group", "_Test Item Group B - 3", "parent_item_group"
)
rgt = frappe.db.get_value("Item Group", parent_item_group, "rgt")
ancestors = get_ancestors_of("Item Group", "_Test Item Group B - 3")
ancestors = frappe.db.sql("""select name, rgt from `tabItem Group`
where name in ({})""".format(", ".join(["%s"]*len(ancestors))), tuple(ancestors), as_dict=True)
ancestors = frappe.db.sql(
"""select name, rgt from `tabItem Group`
where name in ({})""".format(
", ".join(["%s"] * len(ancestors))
),
tuple(ancestors),
as_dict=True,
)
frappe.delete_doc("Item Group", "_Test Item Group B - 3")
records_to_test = test_records[2:]
@@ -173,7 +188,9 @@ class TestItem(unittest.TestCase):
def test_delete_group(self):
# cannot delete group with child, but can delete leaf
self.assertRaises(NestedSetChildExistsError, frappe.delete_doc, "Item Group", "_Test Item Group B")
self.assertRaises(
NestedSetChildExistsError, frappe.delete_doc, "Item Group", "_Test Item Group B"
)
def test_merge_groups(self):
frappe.rename_doc("Item Group", "_Test Item Group B", "_Test Item Group C", merge=True)
@@ -186,8 +203,10 @@ class TestItem(unittest.TestCase):
self.test_basic_tree()
# move its children back
for name in frappe.db.sql_list("""select name from `tabItem Group`
where parent_item_group='_Test Item Group C'"""):
for name in frappe.db.sql_list(
"""select name from `tabItem Group`
where parent_item_group='_Test Item Group C'"""
):
doc = frappe.get_doc("Item Group", name)
doc.parent_item_group = "_Test Item Group B"
@@ -206,9 +225,21 @@ class TestItem(unittest.TestCase):
self.test_basic_tree()
def test_merge_leaf_into_group(self):
self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B - 3",
"_Test Item Group B", merge=True)
self.assertRaises(
NestedSetInvalidMergeError,
frappe.rename_doc,
"Item Group",
"_Test Item Group B - 3",
"_Test Item Group B",
merge=True,
)
def test_merge_group_into_leaf(self):
self.assertRaises(NestedSetInvalidMergeError, frappe.rename_doc, "Item Group", "_Test Item Group B",
"_Test Item Group B - 3", merge=True)
self.assertRaises(
NestedSetInvalidMergeError,
frappe.rename_doc,
"Item Group",
"_Test Item Group B",
"_Test Item Group B - 3",
merge=True,
)

View File

@@ -11,15 +11,25 @@ from frappe.permissions import get_doctypes_with_read
from frappe.utils import cint, cstr
class NamingSeriesNotSetError(frappe.ValidationError): pass
class NamingSeriesNotSetError(frappe.ValidationError):
pass
class NamingSeries(Document):
@frappe.whitelist()
def get_transactions(self, arg=None):
doctypes = list(set(frappe.db.sql_list("""select parent
from `tabDocField` df where fieldname='naming_series'""")
+ frappe.db.sql_list("""select dt from `tabCustom Field`
where fieldname='naming_series'""")))
doctypes = list(
set(
frappe.db.sql_list(
"""select parent
from `tabDocField` df where fieldname='naming_series'"""
)
+ frappe.db.sql_list(
"""select dt from `tabCustom Field`
where fieldname='naming_series'"""
)
)
)
doctypes = list(set(get_doctypes_with_read()).intersection(set(doctypes)))
prefixes = ""
@@ -28,8 +38,8 @@ class NamingSeries(Document):
try:
options = self.get_options(d)
except frappe.DoesNotExistError:
frappe.msgprint(_('Unable to find DocType {0}').format(d))
#frappe.pass_does_not_exist_error()
frappe.msgprint(_("Unable to find DocType {0}").format(d))
# frappe.pass_does_not_exist_error()
continue
if options:
@@ -37,17 +47,21 @@ class NamingSeries(Document):
prefixes.replace("\n\n", "\n")
prefixes = prefixes.split("\n")
custom_prefixes = frappe.get_all('DocType', fields=["autoname"],
filters={"name": ('not in', doctypes), "autoname":('like', '%.#%'), 'module': ('not in', ['Core'])})
custom_prefixes = frappe.get_all(
"DocType",
fields=["autoname"],
filters={
"name": ("not in", doctypes),
"autoname": ("like", "%.#%"),
"module": ("not in", ["Core"]),
},
)
if custom_prefixes:
prefixes = prefixes + [d.autoname.rsplit('.', 1)[0] for d in custom_prefixes]
prefixes = prefixes + [d.autoname.rsplit(".", 1)[0] for d in custom_prefixes]
prefixes = "\n".join(sorted(prefixes))
return {
"transactions": "\n".join([''] + sorted(doctypes)),
"prefixes": prefixes
}
return {"transactions": "\n".join([""] + sorted(doctypes)), "prefixes": prefixes}
def scrub_options_list(self, ol):
options = list(filter(lambda x: x, [cstr(n).strip() for n in ol]))
@@ -64,7 +78,7 @@ class NamingSeries(Document):
self.set_series_for(self.select_doc_for_series, series_list)
# create series
map(self.insert_series, [d.split('.')[0] for d in series_list if d.strip()])
map(self.insert_series, [d.split(".")[0] for d in series_list if d.strip()])
msgprint(_("Series Updated"))
@@ -82,32 +96,35 @@ class NamingSeries(Document):
self.validate_series_name(i)
if options and self.user_must_always_select:
options = [''] + options
options = [""] + options
default = options[0] if options else ''
default = options[0] if options else ""
# update in property setter
prop_dict = {'options': "\n".join(options), 'default': default}
prop_dict = {"options": "\n".join(options), "default": default}
for prop in prop_dict:
ps_exists = frappe.db.get_value("Property Setter",
{"field_name": 'naming_series', 'doc_type': doctype, 'property': prop})
ps_exists = frappe.db.get_value(
"Property Setter", {"field_name": "naming_series", "doc_type": doctype, "property": prop}
)
if ps_exists:
ps = frappe.get_doc('Property Setter', ps_exists)
ps = frappe.get_doc("Property Setter", ps_exists)
ps.value = prop_dict[prop]
ps.save()
else:
ps = frappe.get_doc({
'doctype': 'Property Setter',
'doctype_or_field': 'DocField',
'doc_type': doctype,
'field_name': 'naming_series',
'property': prop,
'value': prop_dict[prop],
'property_type': 'Text',
'__islocal': 1
})
ps = frappe.get_doc(
{
"doctype": "Property Setter",
"doctype_or_field": "DocField",
"doc_type": doctype,
"field_name": "naming_series",
"property": prop,
"value": prop_dict[prop],
"property_type": "Text",
"__islocal": 1,
}
)
ps.save()
self.set_options = "\n".join(options)
@@ -115,16 +132,22 @@ class NamingSeries(Document):
frappe.clear_cache(doctype=doctype)
def check_duplicate(self):
parent = list(set(
frappe.db.sql_list("""select dt.name
parent = list(
set(
frappe.db.sql_list(
"""select dt.name
from `tabDocField` df, `tabDocType` dt
where dt.name = df.parent and df.fieldname='naming_series' and dt.name != %s""",
self.select_doc_for_series)
+ frappe.db.sql_list("""select dt.name
self.select_doc_for_series,
)
+ frappe.db.sql_list(
"""select dt.name
from `tabCustom Field` df, `tabDocType` dt
where dt.name = df.dt and df.fieldname='naming_series' and dt.name != %s""",
self.select_doc_for_series)
))
self.select_doc_for_series,
)
)
)
sr = [[frappe.get_meta(p).get_field("naming_series").options, p] for p in parent]
dt = frappe.get_doc("DocType", self.select_doc_for_series)
options = self.scrub_options_list(self.set_options.split("\n"))
@@ -132,14 +155,17 @@ class NamingSeries(Document):
validate_series(dt, series)
for i in sr:
if i[0]:
existing_series = [d.split('.')[0] for d in i[0].split("\n")]
existing_series = [d.split(".")[0] for d in i[0].split("\n")]
if series.split(".")[0] in existing_series:
frappe.throw(_("Series {0} already used in {1}").format(series,i[1]))
frappe.throw(_("Series {0} already used in {1}").format(series, i[1]))
def validate_series_name(self, n):
import re
if not re.match(r"^[\w\- \/.#{}]+$", n, re.UNICODE):
throw(_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series'))
throw(
_('Special Characters except "-", "#", ".", "/", "{" and "}" not allowed in naming series')
)
@frappe.whitelist()
def get_options(self, arg=None):
@@ -151,12 +177,11 @@ class NamingSeries(Document):
"""get series current"""
if self.prefix:
prefix = self.parse_naming_series()
self.current_value = frappe.db.get_value("Series",
prefix, "current", order_by = "name")
self.current_value = frappe.db.get_value("Series", prefix, "current", order_by="name")
def insert_series(self, series):
"""insert series if missing"""
if frappe.db.get_value('Series', series, 'name', order_by="name") == None:
if frappe.db.get_value("Series", series, "name", order_by="name") == None:
frappe.db.sql("insert into tabSeries (name, current) values (%s, 0)", (series))
@frappe.whitelist()
@@ -164,14 +189,15 @@ class NamingSeries(Document):
if self.prefix:
prefix = self.parse_naming_series()
self.insert_series(prefix)
frappe.db.sql("update `tabSeries` set current = %s where name = %s",
(cint(self.current_value), prefix))
frappe.db.sql(
"update `tabSeries` set current = %s where name = %s", (cint(self.current_value), prefix)
)
msgprint(_("Series Updated Successfully"))
else:
msgprint(_("Please select prefix first"))
def parse_naming_series(self):
parts = self.prefix.split('.')
parts = self.prefix.split(".")
# Remove ### from the end of series
if parts[-1] == "#" * len(parts[-1]):
@@ -180,34 +206,59 @@ class NamingSeries(Document):
prefix = parse_naming_series(parts)
return prefix
def set_by_naming_series(doctype, fieldname, naming_series, hide_name_field=True, make_mandatory=1):
def set_by_naming_series(
doctype, fieldname, naming_series, hide_name_field=True, make_mandatory=1
):
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
if naming_series:
make_property_setter(doctype, "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, "naming_series", "reqd", make_mandatory, "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype, "naming_series", "hidden", 0, "Check", validate_fields_for_doctype=False
)
make_property_setter(
doctype, "naming_series", "reqd", make_mandatory, "Check", validate_fields_for_doctype=False
)
# set values for mandatory
try:
frappe.db.sql("""update `tab{doctype}` set naming_series={s} where
ifnull(naming_series, '')=''""".format(doctype=doctype, s="%s"),
get_default_naming_series(doctype))
frappe.db.sql(
"""update `tab{doctype}` set naming_series={s} where
ifnull(naming_series, '')=''""".format(
doctype=doctype, s="%s"
),
get_default_naming_series(doctype),
)
except NamingSeriesNotSetError:
pass
if hide_name_field:
make_property_setter(doctype, fieldname, "reqd", 0, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, fieldname, "hidden", 1, "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype, fieldname, "hidden", 1, "Check", validate_fields_for_doctype=False
)
else:
make_property_setter(doctype, "naming_series", "reqd", 0, "Check", validate_fields_for_doctype=False)
make_property_setter(doctype, "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype, "naming_series", "reqd", 0, "Check", validate_fields_for_doctype=False
)
make_property_setter(
doctype, "naming_series", "hidden", 1, "Check", validate_fields_for_doctype=False
)
if hide_name_field:
make_property_setter(doctype, fieldname, "hidden", 0, "Check", validate_fields_for_doctype=False)
make_property_setter(
doctype, fieldname, "hidden", 0, "Check", validate_fields_for_doctype=False
)
make_property_setter(doctype, fieldname, "reqd", 1, "Check", validate_fields_for_doctype=False)
# set values for mandatory
frappe.db.sql("""update `tab{doctype}` set `{fieldname}`=`name` where
ifnull({fieldname}, '')=''""".format(doctype=doctype, fieldname=fieldname))
frappe.db.sql(
"""update `tab{doctype}` set `{fieldname}`=`name` where
ifnull({fieldname}, '')=''""".format(
doctype=doctype, fieldname=fieldname
)
)
def get_default_naming_series(doctype):
naming_series = frappe.get_meta(doctype).get_field("naming_series").options or ""
@@ -215,7 +266,9 @@ def get_default_naming_series(doctype):
out = naming_series[0] or (naming_series[1] if len(naming_series) > 1 else None)
if not out:
frappe.throw(_("Please set Naming Series for {0} via Setup > Settings > Naming Series").format(doctype),
NamingSeriesNotSetError)
frappe.throw(
_("Please set Naming Series for {0} via Setup > Settings > Naming Series").format(doctype),
NamingSeriesNotSetError,
)
else:
return out

View File

@@ -9,18 +9,20 @@ from frappe.model.document import Document
class PartyType(Document):
pass
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_party_type(doctype, txt, searchfield, start, page_len, filters):
cond = ''
if filters and filters.get('account'):
account_type = frappe.db.get_value('Account', filters.get('account'), 'account_type')
cond = ""
if filters and filters.get("account"):
account_type = frappe.db.get_value("Account", filters.get("account"), "account_type")
cond = "and account_type = '%s'" % account_type
return frappe.db.sql("""select name from `tabParty Type`
return frappe.db.sql(
"""select name from `tabParty Type`
where `{key}` LIKE %(txt)s {cond}
order by name limit %(start)s, %(page_len)s"""
.format(key=searchfield, cond=cond), {
'txt': '%' + txt + '%',
'start': start, 'page_len': page_len
})
order by name limit %(start)s, %(page_len)s""".format(
key=searchfield, cond=cond
),
{"txt": "%" + txt + "%", "start": start, "page_len": page_len},
)

View File

@@ -5,5 +5,6 @@ import unittest
# test_records = frappe.get_test_records('Party Type')
class TestPartyType(unittest.TestCase):
pass

View File

@@ -3,4 +3,4 @@
import frappe
test_records = frappe.get_test_records('Print Heading')
test_records = frappe.get_test_records("Print Heading")

View File

@@ -3,4 +3,4 @@
import frappe
test_records = frappe.get_test_records('Quotation Lost Reason')
test_records = frappe.get_test_records("Quotation Lost Reason")

View File

@@ -10,9 +10,9 @@ from frappe.website.website_generator import WebsiteGenerator
class SalesPartner(WebsiteGenerator):
website = frappe._dict(
page_title_field = "partner_name",
condition_field = "show_in_website",
template = "templates/generators/sales_partner.html"
page_title_field="partner_name",
condition_field="show_in_website",
template="templates/generators/sales_partner.html",
)
def onload(self):
@@ -30,18 +30,25 @@ class SalesPartner(WebsiteGenerator):
self.partner_website = "http://" + self.partner_website
def get_context(self, context):
address = frappe.db.get_value("Address",
{"sales_partner": self.name, "is_primary_address": 1},
"*", as_dict=True)
address = frappe.db.get_value(
"Address", {"sales_partner": self.name, "is_primary_address": 1}, "*", as_dict=True
)
if address:
city_state = ", ".join(filter(None, [address.city, address.state]))
address_rows = [address.address_line1, address.address_line2,
city_state, address.pincode, address.country]
address_rows = [
address.address_line1,
address.address_line2,
city_state,
address.pincode,
address.country,
]
context.update({
"email": address.email_id,
"partner_address": filter_strip_join(address_rows, "\n<br>"),
"phone": filter_strip_join(cstr(address.phone).split(","), "\n<br>")
})
context.update(
{
"email": address.email_id,
"partner_address": filter_strip_join(address_rows, "\n<br>"),
"phone": filter_strip_join(cstr(address.phone).split(","), "\n<br>"),
}
)
return context

View File

@@ -3,6 +3,6 @@
import frappe
test_records = frappe.get_test_records('Sales Partner')
test_records = frappe.get_test_records("Sales Partner")
test_ignore = ["Item Group"]

View File

@@ -11,13 +11,13 @@ from erpnext import get_default_currency
class SalesPerson(NestedSet):
nsm_parent_field = 'parent_sales_person'
nsm_parent_field = "parent_sales_person"
def validate(self):
if not self.parent_sales_person:
self.parent_sales_person = get_root_of("Sales Person")
for d in self.get('targets') or []:
for d in self.get("targets") or []:
if not flt(d.target_qty) and not flt(d.target_amount):
frappe.throw(_("Either target qty or target amount is mandatory."))
self.validate_employee_id()
@@ -28,17 +28,20 @@ class SalesPerson(NestedSet):
def load_dashboard_info(self):
company_default_currency = get_default_currency()
allocated_amount = frappe.db.sql("""
allocated_amount = frappe.db.sql(
"""
select sum(allocated_amount)
from `tabSales Team`
where sales_person = %s and docstatus=1 and parenttype = 'Sales Order'
""",(self.sales_person_name))
""",
(self.sales_person_name),
)
info = {}
info["allocated_amount"] = flt(allocated_amount[0][0]) if allocated_amount else 0
info["currency"] = company_default_currency
self.set_onload('dashboard_info', info)
self.set_onload("dashboard_info", info)
def on_update(self):
super(SalesPerson, self).on_update()
@@ -57,30 +60,46 @@ class SalesPerson(NestedSet):
sales_person = frappe.db.get_value("Sales Person", {"employee": self.employee})
if sales_person and sales_person != self.name:
frappe.throw(_("Another Sales Person {0} exists with the same Employee id").format(sales_person))
frappe.throw(
_("Another Sales Person {0} exists with the same Employee id").format(sales_person)
)
def on_doctype_update():
frappe.db.add_index("Sales Person", ["lft", "rgt"])
def get_timeline_data(doctype, name):
out = {}
out.update(dict(frappe.db.sql('''select
out.update(
dict(
frappe.db.sql(
"""select
unix_timestamp(dt.transaction_date), count(st.parenttype)
from
`tabSales Order` dt, `tabSales Team` st
where
st.sales_person = %s and st.parent = dt.name and dt.transaction_date > date_sub(curdate(), interval 1 year)
group by dt.transaction_date ''', name)))
group by dt.transaction_date """,
name,
)
)
)
sales_invoice = dict(frappe.db.sql('''select
sales_invoice = dict(
frappe.db.sql(
"""select
unix_timestamp(dt.posting_date), count(st.parenttype)
from
`tabSales Invoice` dt, `tabSales Team` st
where
st.sales_person = %s and st.parent = dt.name and dt.posting_date > date_sub(curdate(), interval 1 year)
group by dt.posting_date ''', name))
group by dt.posting_date """,
name,
)
)
for key in sales_invoice:
if out.get(key):
@@ -88,13 +107,18 @@ def get_timeline_data(doctype, name):
else:
out[key] = sales_invoice[key]
delivery_note = dict(frappe.db.sql('''select
delivery_note = dict(
frappe.db.sql(
"""select
unix_timestamp(dt.posting_date), count(st.parenttype)
from
`tabDelivery Note` dt, `tabSales Team` st
where
st.sales_person = %s and st.parent = dt.name and dt.posting_date > date_sub(curdate(), interval 1 year)
group by dt.posting_date ''', name))
group by dt.posting_date """,
name,
)
)
for key in delivery_note:
if out.get(key):

View File

@@ -1,16 +1,14 @@
from frappe import _
def get_data():
return {
'heatmap': True,
'heatmap_message': _('This is based on transactions against this Sales Person. See timeline below for details'),
'fieldname': 'sales_person',
'transactions': [
{
'label': _('Sales'),
'items': ['Sales Order', 'Delivery Note', 'Sales Invoice']
},
]
"heatmap": True,
"heatmap_message": _(
"This is based on transactions against this Sales Person. See timeline below for details"
),
"fieldname": "sales_person",
"transactions": [
{"label": _("Sales"), "items": ["Sales Order", "Delivery Note", "Sales Invoice"]},
],
}

View File

@@ -5,6 +5,6 @@ test_dependencies = ["Employee"]
import frappe
test_records = frappe.get_test_records('Sales Person')
test_records = frappe.get_test_records("Sales Person")
test_ignore = ["Item Group"]

View File

@@ -7,7 +7,7 @@ from frappe.utils.nestedset import NestedSet, get_root_of
class SupplierGroup(NestedSet):
nsm_parent_field = 'parent_supplier_group'
nsm_parent_field = "parent_supplier_group"
def validate(self):
if not self.parent_supplier_group:

View File

@@ -3,4 +3,4 @@
import frappe
test_records = frappe.get_test_records('Supplier Group')
test_records = frappe.get_test_records("Supplier Group")

View File

@@ -16,9 +16,15 @@ class TermsandConditions(Document):
def validate(self):
if self.terms:
validate_template(self.terms)
if not cint(self.buying) and not cint(self.selling) and not cint(self.hr) and not cint(self.disabled):
if (
not cint(self.buying)
and not cint(self.selling)
and not cint(self.hr)
and not cint(self.disabled)
):
throw(_("At least one of the Applicable Modules should be selected"))
@frappe.whitelist()
def get_terms_and_conditions(template_name, doc):
if isinstance(doc, string_types):

View File

@@ -3,4 +3,4 @@
import frappe
test_records = frappe.get_test_records('Terms and Conditions')
test_records = frappe.get_test_records("Terms and Conditions")

View File

@@ -9,13 +9,13 @@ from frappe.utils.nestedset import NestedSet, get_root_of
class Territory(NestedSet):
nsm_parent_field = 'parent_territory'
nsm_parent_field = "parent_territory"
def validate(self):
if not self.parent_territory:
self.parent_territory = get_root_of("Territory")
for d in self.get('targets') or []:
for d in self.get("targets") or []:
if not flt(d.target_qty) and not flt(d.target_amount):
frappe.throw(_("Either target qty or target amount is mandatory"))
@@ -23,5 +23,6 @@ class Territory(NestedSet):
super(Territory, self).on_update()
self.validate_one_root()
def on_doctype_update():
frappe.db.add_index("Territory", ["lft", "rgt"])

View File

@@ -3,6 +3,6 @@
import frappe
test_records = frappe.get_test_records('Territory')
test_records = frappe.get_test_records("Territory")
test_ignore = ["Item Group"]

View File

@@ -8,61 +8,51 @@ import frappe
class TestTransactionDeletionRecord(unittest.TestCase):
def setUp(self):
create_company('Dunder Mifflin Paper Co')
create_company("Dunder Mifflin Paper Co")
def tearDown(self):
frappe.db.rollback()
def test_doctypes_contain_company_field(self):
tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co")
for doctype in tdr.doctypes:
contains_company = False
doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()['fields']
doctype_fields = frappe.get_meta(doctype.doctype_name).as_dict()["fields"]
for doctype_field in doctype_fields:
if doctype_field['fieldtype'] == 'Link' and doctype_field['options'] == 'Company':
if doctype_field["fieldtype"] == "Link" and doctype_field["options"] == "Company":
contains_company = True
break
self.assertTrue(contains_company)
def test_no_of_docs_is_correct(self):
for i in range(5):
create_task('Dunder Mifflin Paper Co')
tdr = create_transaction_deletion_request('Dunder Mifflin Paper Co')
create_task("Dunder Mifflin Paper Co")
tdr = create_transaction_deletion_request("Dunder Mifflin Paper Co")
for doctype in tdr.doctypes:
if doctype.doctype_name == 'Task':
if doctype.doctype_name == "Task":
self.assertEqual(doctype.no_of_docs, 5)
def test_deletion_is_successful(self):
create_task('Dunder Mifflin Paper Co')
create_transaction_deletion_request('Dunder Mifflin Paper Co')
tasks_containing_company = frappe.get_all('Task',
filters = {
'company' : 'Dunder Mifflin Paper Co'
})
create_task("Dunder Mifflin Paper Co")
create_transaction_deletion_request("Dunder Mifflin Paper Co")
tasks_containing_company = frappe.get_all("Task", filters={"company": "Dunder Mifflin Paper Co"})
self.assertEqual(tasks_containing_company, [])
def create_company(company_name):
company = frappe.get_doc({
'doctype': 'Company',
'company_name': company_name,
'default_currency': 'INR'
})
company.insert(ignore_if_duplicate = True)
company = frappe.get_doc(
{"doctype": "Company", "company_name": company_name, "default_currency": "INR"}
)
company.insert(ignore_if_duplicate=True)
def create_transaction_deletion_request(company):
tdr = frappe.get_doc({
'doctype': 'Transaction Deletion Record',
'company': company
})
tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
tdr.insert()
tdr.submit()
return tdr
def create_task(company):
task = frappe.get_doc({
'doctype': 'Task',
'company': company,
'subject': 'Delete'
})
task = frappe.get_doc({"doctype": "Task", "company": company, "subject": "Delete"})
task.insert()

View File

@@ -11,15 +11,19 @@ from frappe.utils import cint
class TransactionDeletionRecord(Document):
def validate(self):
frappe.only_for('System Manager')
frappe.only_for("System Manager")
self.validate_doctypes_to_be_ignored()
def validate_doctypes_to_be_ignored(self):
doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
for doctype in self.doctypes_to_be_ignored:
if doctype.doctype_name not in doctypes_to_be_ignored_list:
frappe.throw(_("DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."),
title=_("Not Allowed"))
frappe.throw(
_(
"DocTypes should not be added manually to the 'Excluded DocTypes' table. You are only allowed to remove entries from it."
),
title=_("Not Allowed"),
)
def before_submit(self):
if not self.doctypes_to_be_ignored:
@@ -34,38 +38,55 @@ class TransactionDeletionRecord(Document):
def populate_doctypes_to_be_ignored_table(self):
doctypes_to_be_ignored_list = get_doctypes_to_be_ignored()
for doctype in doctypes_to_be_ignored_list:
self.append('doctypes_to_be_ignored', {
'doctype_name' : doctype
})
self.append("doctypes_to_be_ignored", {"doctype_name": doctype})
def delete_bins(self):
frappe.db.sql("""delete from tabBin where warehouse in
(select name from tabWarehouse where company=%s)""", self.company)
frappe.db.sql(
"""delete from tabBin where warehouse in
(select name from tabWarehouse where company=%s)""",
self.company,
)
def delete_lead_addresses(self):
"""Delete addresses to which leads are linked"""
leads = frappe.get_all('Lead', filters={'company': self.company})
leads = frappe.get_all("Lead", filters={"company": self.company})
leads = ["'%s'" % row.get("name") for row in leads]
addresses = []
if leads:
addresses = frappe.db.sql_list("""select parent from `tabDynamic Link` where link_name
in ({leads})""".format(leads=",".join(leads)))
addresses = frappe.db.sql_list(
"""select parent from `tabDynamic Link` where link_name
in ({leads})""".format(
leads=",".join(leads)
)
)
if addresses:
addresses = ["%s" % frappe.db.escape(addr) for addr in addresses]
frappe.db.sql("""delete from tabAddress where name in ({addresses}) and
frappe.db.sql(
"""delete from tabAddress where name in ({addresses}) and
name not in (select distinct dl1.parent from `tabDynamic Link` dl1
inner join `tabDynamic Link` dl2 on dl1.parent=dl2.parent
and dl1.link_doctype<>dl2.link_doctype)""".format(addresses=",".join(addresses)))
and dl1.link_doctype<>dl2.link_doctype)""".format(
addresses=",".join(addresses)
)
)
frappe.db.sql("""delete from `tabDynamic Link` where link_doctype='Lead'
and parenttype='Address' and link_name in ({leads})""".format(leads=",".join(leads)))
frappe.db.sql(
"""delete from `tabDynamic Link` where link_doctype='Lead'
and parenttype='Address' and link_name in ({leads})""".format(
leads=",".join(leads)
)
)
frappe.db.sql("""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(leads=",".join(leads)))
frappe.db.sql(
"""update tabCustomer set lead_name=NULL where lead_name in ({leads})""".format(
leads=",".join(leads)
)
)
def reset_company_values(self):
company_obj = frappe.get_doc('Company', self.company)
company_obj = frappe.get_doc("Company", self.company)
company_obj.total_monthly_sales = 0
company_obj.sales_monthly_history = None
company_obj.save()
@@ -76,24 +97,26 @@ class TransactionDeletionRecord(Document):
tables = self.get_all_child_doctypes()
for docfield in docfields:
if docfield['parent'] != self.doctype:
no_of_docs = self.get_number_of_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
if docfield["parent"] != self.doctype:
no_of_docs = self.get_number_of_docs_linked_with_specified_company(
docfield["parent"], docfield["fieldname"]
)
if no_of_docs > 0:
self.delete_version_log(docfield['parent'], docfield['fieldname'])
self.delete_communications(docfield['parent'], docfield['fieldname'])
self.populate_doctypes_table(tables, docfield['parent'], no_of_docs)
self.delete_version_log(docfield["parent"], docfield["fieldname"])
self.delete_communications(docfield["parent"], docfield["fieldname"])
self.populate_doctypes_table(tables, docfield["parent"], no_of_docs)
self.delete_child_tables(docfield['parent'], docfield['fieldname'])
self.delete_docs_linked_with_specified_company(docfield['parent'], docfield['fieldname'])
self.delete_child_tables(docfield["parent"], docfield["fieldname"])
self.delete_docs_linked_with_specified_company(docfield["parent"], docfield["fieldname"])
naming_series = frappe.db.get_value('DocType', docfield['parent'], 'autoname')
naming_series = frappe.db.get_value("DocType", docfield["parent"], "autoname")
if naming_series:
if '#' in naming_series:
self.update_naming_series(naming_series, docfield['parent'])
if "#" in naming_series:
self.update_naming_series(naming_series, docfield["parent"])
def get_doctypes_to_be_ignored_list(self):
singles = frappe.get_all('DocType', filters = {'issingle': 1}, pluck = 'name')
singles = frappe.get_all("DocType", filters={"issingle": 1}, pluck="name")
doctypes_to_be_ignored_list = singles
for doctype in self.doctypes_to_be_ignored:
doctypes_to_be_ignored_list.append(doctype.doctype_name)
@@ -101,81 +124,104 @@ class TransactionDeletionRecord(Document):
return doctypes_to_be_ignored_list
def get_doctypes_with_company_field(self, doctypes_to_be_ignored_list):
docfields = frappe.get_all('DocField',
filters = {
'fieldtype': 'Link',
'options': 'Company',
'parent': ['not in', doctypes_to_be_ignored_list]},
fields=['parent', 'fieldname'])
docfields = frappe.get_all(
"DocField",
filters={
"fieldtype": "Link",
"options": "Company",
"parent": ["not in", doctypes_to_be_ignored_list],
},
fields=["parent", "fieldname"],
)
return docfields
def get_all_child_doctypes(self):
return frappe.get_all('DocType', filters = {'istable': 1}, pluck = 'name')
return frappe.get_all("DocType", filters={"istable": 1}, pluck="name")
def get_number_of_docs_linked_with_specified_company(self, doctype, company_fieldname):
return frappe.db.count(doctype, {company_fieldname : self.company})
return frappe.db.count(doctype, {company_fieldname: self.company})
def populate_doctypes_table(self, tables, doctype, no_of_docs):
if doctype not in tables:
self.append('doctypes', {
'doctype_name' : doctype,
'no_of_docs' : no_of_docs
})
self.append("doctypes", {"doctype_name": doctype, "no_of_docs": no_of_docs})
def delete_child_tables(self, doctype, company_fieldname):
parent_docs_to_be_deleted = frappe.get_all(doctype, {
company_fieldname : self.company
}, pluck = 'name')
parent_docs_to_be_deleted = frappe.get_all(
doctype, {company_fieldname: self.company}, pluck="name"
)
child_tables = frappe.get_all('DocField', filters = {
'fieldtype': 'Table',
'parent': doctype
}, pluck = 'options')
child_tables = frappe.get_all(
"DocField", filters={"fieldtype": "Table", "parent": doctype}, pluck="options"
)
for table in child_tables:
frappe.db.delete(table, {
'parent': ['in', parent_docs_to_be_deleted]
})
frappe.db.delete(table, {"parent": ["in", parent_docs_to_be_deleted]})
def delete_docs_linked_with_specified_company(self, doctype, company_fieldname):
frappe.db.delete(doctype, {
company_fieldname : self.company
})
frappe.db.delete(doctype, {company_fieldname: self.company})
def update_naming_series(self, naming_series, doctype_name):
if '.' in naming_series:
prefix, hashes = naming_series.rsplit('.', 1)
if "." in naming_series:
prefix, hashes = naming_series.rsplit(".", 1)
else:
prefix, hashes = naming_series.rsplit('{', 1)
last = frappe.db.sql("""select max(name) from `tab{0}`
where name like %s""".format(doctype_name), prefix + '%')
prefix, hashes = naming_series.rsplit("{", 1)
last = frappe.db.sql(
"""select max(name) from `tab{0}`
where name like %s""".format(
doctype_name
),
prefix + "%",
)
if last and last[0][0]:
last = cint(last[0][0].replace(prefix, ''))
last = cint(last[0][0].replace(prefix, ""))
else:
last = 0
frappe.db.sql("""update tabSeries set current = %s where name=%s""", (last, prefix))
def delete_version_log(self, doctype, company_fieldname):
frappe.db.sql("""delete from `tabVersion` where ref_doctype=%s and docname in
(select name from `tab{0}` where `{1}`=%s)""".format(doctype,
company_fieldname), (doctype, self.company))
frappe.db.sql(
"""delete from `tabVersion` where ref_doctype=%s and docname in
(select name from `tab{0}` where `{1}`=%s)""".format(
doctype, company_fieldname
),
(doctype, self.company),
)
def delete_communications(self, doctype, company_fieldname):
reference_docs = frappe.get_all(doctype, filters={company_fieldname:self.company})
reference_docs = frappe.get_all(doctype, filters={company_fieldname: self.company})
reference_doc_names = [r.name for r in reference_docs]
communications = frappe.get_all('Communication', filters={'reference_doctype':doctype,'reference_name':['in', reference_doc_names]})
communications = frappe.get_all(
"Communication",
filters={"reference_doctype": doctype, "reference_name": ["in", reference_doc_names]},
)
communication_names = [c.name for c in communications]
frappe.delete_doc('Communication', communication_names, ignore_permissions=True)
frappe.delete_doc("Communication", communication_names, ignore_permissions=True)
@frappe.whitelist()
def get_doctypes_to_be_ignored():
doctypes_to_be_ignored_list = ['Account', 'Cost Center', 'Warehouse', 'Budget',
'Party Account', 'Employee', 'Sales Taxes and Charges Template',
'Purchase Taxes and Charges Template', 'POS Profile', 'BOM',
'Company', 'Bank Account', 'Item Tax Template', 'Mode of Payment',
'Item Default', 'Customer', 'Supplier', 'GST Account']
doctypes_to_be_ignored_list = [
"Account",
"Cost Center",
"Warehouse",
"Budget",
"Party Account",
"Employee",
"Sales Taxes and Charges Template",
"Purchase Taxes and Charges Template",
"POS Profile",
"BOM",
"Company",
"Bank Account",
"Item Tax Template",
"Mode of Payment",
"Item Default",
"Customer",
"Supplier",
"GST Account",
]
return doctypes_to_be_ignored_list

View File

@@ -3,4 +3,4 @@
import frappe
test_records = frappe.get_test_records('UOM')
test_records = frappe.get_test_records("UOM")

View File

@@ -20,7 +20,7 @@ default_mail_footer = """<div style="padding: 7px; text-align: right; color: #88
def after_install():
frappe.get_doc({'doctype': "Role", "role_name": "Analytics"}).insert()
frappe.get_doc({"doctype": "Role", "role_name": "Analytics"}).insert()
set_single_defaults()
create_compact_item_print_custom_field()
create_print_uom_after_qty_custom_field()
@@ -37,17 +37,26 @@ def after_install():
def check_setup_wizard_not_completed():
if cint(frappe.db.get_single_value('System Settings', 'setup_complete') or 0):
if cint(frappe.db.get_single_value("System Settings", "setup_complete") or 0):
message = """ERPNext can only be installed on a fresh site where the setup wizard is not completed.
You can reinstall this site (after saving your data) using: bench --site [sitename] reinstall"""
frappe.throw(message) # nosemgrep
frappe.throw(message) # nosemgrep
def set_single_defaults():
for dt in ('Accounts Settings', 'Print Settings', 'HR Settings', 'Buying Settings',
'Selling Settings', 'Stock Settings'):
default_values = frappe.db.sql("""select fieldname, `default` from `tabDocField`
where parent=%s""", dt)
for dt in (
"Accounts Settings",
"Print Settings",
"HR Settings",
"Buying Settings",
"Selling Settings",
"Stock Settings",
):
default_values = frappe.db.sql(
"""select fieldname, `default` from `tabDocField`
where parent=%s""",
dt,
)
if default_values:
try:
b = frappe.get_doc(dt, dt)
@@ -63,111 +72,128 @@ def set_single_defaults():
def create_compact_item_print_custom_field():
create_custom_field('Print Settings', {
'label': _('Compact Item Print'),
'fieldname': 'compact_item_print',
'fieldtype': 'Check',
'default': 1,
'insert_after': 'with_letterhead'
})
create_custom_field(
"Print Settings",
{
"label": _("Compact Item Print"),
"fieldname": "compact_item_print",
"fieldtype": "Check",
"default": 1,
"insert_after": "with_letterhead",
},
)
def create_print_uom_after_qty_custom_field():
create_custom_field('Print Settings', {
'label': _('Print UOM after Quantity'),
'fieldname': 'print_uom_after_quantity',
'fieldtype': 'Check',
'default': 0,
'insert_after': 'compact_item_print'
})
create_custom_field(
"Print Settings",
{
"label": _("Print UOM after Quantity"),
"fieldname": "print_uom_after_quantity",
"fieldtype": "Check",
"default": 0,
"insert_after": "compact_item_print",
},
)
def create_print_zero_amount_taxes_custom_field():
create_custom_field('Print Settings', {
'label': _('Print taxes with zero amount'),
'fieldname': 'print_taxes_with_zero_amount',
'fieldtype': 'Check',
'default': 0,
'insert_after': 'allow_print_for_cancelled'
})
create_custom_field(
"Print Settings",
{
"label": _("Print taxes with zero amount"),
"fieldname": "print_taxes_with_zero_amount",
"fieldtype": "Check",
"default": 0,
"insert_after": "allow_print_for_cancelled",
},
)
def create_default_cash_flow_mapper_templates():
for mapper in DEFAULT_MAPPERS:
if not frappe.db.exists('Cash Flow Mapper', mapper['section_name']):
if not frappe.db.exists("Cash Flow Mapper", mapper["section_name"]):
doc = frappe.get_doc(mapper)
doc.insert(ignore_permissions=True)
def create_default_success_action():
for success_action in get_default_success_action():
if not frappe.db.exists('Success Action', success_action.get("ref_doctype")):
if not frappe.db.exists("Success Action", success_action.get("ref_doctype")):
doc = frappe.get_doc(success_action)
doc.insert(ignore_permissions=True)
def create_default_energy_point_rules():
for rule in get_default_energy_point_rules():
# check if any rule for ref. doctype exists
rule_exists = frappe.db.exists('Energy Point Rule', {
'reference_doctype': rule.get('reference_doctype')
})
if rule_exists: continue
rule_exists = frappe.db.exists(
"Energy Point Rule", {"reference_doctype": rule.get("reference_doctype")}
)
if rule_exists:
continue
doc = frappe.get_doc(rule)
doc.insert(ignore_permissions=True)
def add_company_to_session_defaults():
settings = frappe.get_single("Session Default Settings")
settings.append("session_defaults", {
"ref_doctype": "Company"
})
settings.append("session_defaults", {"ref_doctype": "Company"})
settings.save()
def add_standard_navbar_items():
navbar_settings = frappe.get_single("Navbar Settings")
erpnext_navbar_items = [
{
'item_label': 'Documentation',
'item_type': 'Route',
'route': 'https://erpnext.com/docs/user/manual',
'is_standard': 1
"item_label": "Documentation",
"item_type": "Route",
"route": "https://erpnext.com/docs/user/manual",
"is_standard": 1,
},
{
'item_label': 'User Forum',
'item_type': 'Route',
'route': 'https://discuss.erpnext.com',
'is_standard': 1
"item_label": "User Forum",
"item_type": "Route",
"route": "https://discuss.erpnext.com",
"is_standard": 1,
},
{
'item_label': 'Report an Issue',
'item_type': 'Route',
'route': 'https://github.com/frappe/erpnext/issues',
'is_standard': 1
}
"item_label": "Report an Issue",
"item_type": "Route",
"route": "https://github.com/frappe/erpnext/issues",
"is_standard": 1,
},
]
current_navbar_items = navbar_settings.help_dropdown
navbar_settings.set('help_dropdown', [])
navbar_settings.set("help_dropdown", [])
for item in erpnext_navbar_items:
current_labels = [item.get('item_label') for item in current_navbar_items]
if not item.get('item_label') in current_labels:
navbar_settings.append('help_dropdown', item)
current_labels = [item.get("item_label") for item in current_navbar_items]
if not item.get("item_label") in current_labels:
navbar_settings.append("help_dropdown", item)
for item in current_navbar_items:
navbar_settings.append('help_dropdown', {
'item_label': item.item_label,
'item_type': item.item_type,
'route': item.route,
'action': item.action,
'is_standard': item.is_standard,
'hidden': item.hidden
})
navbar_settings.append(
"help_dropdown",
{
"item_label": item.item_label,
"item_type": item.item_type,
"route": item.route,
"action": item.action,
"is_standard": item.is_standard,
"hidden": item.hidden,
},
)
navbar_settings.save()
def add_app_name():
frappe.db.set_value('System Settings', None, 'app_name', 'ERPNext')
frappe.db.set_value("System Settings", None, "app_name", "ERPNext")
def add_non_standard_user_types():
user_types = get_user_types_data()
@@ -176,73 +202,77 @@ def add_non_standard_user_types():
for user_type, data in iteritems(user_types):
user_type_limit.setdefault(frappe.scrub(user_type), 10)
update_site_config('user_type_doctype_limit', user_type_limit)
update_site_config("user_type_doctype_limit", user_type_limit)
for user_type, data in iteritems(user_types):
create_custom_role(data)
create_user_type(user_type, data)
def get_user_types_data():
return {
'Employee Self Service': {
'role': 'Employee Self Service',
'apply_user_permission_on': 'Employee',
'user_id_field': 'user_id',
'doctypes': {
'Salary Slip': ['read'],
'Employee': ['read', 'write'],
'Expense Claim': ['read', 'write', 'create', 'delete'],
'Leave Application': ['read', 'write', 'create', 'delete'],
'Attendance Request': ['read', 'write', 'create', 'delete'],
'Compensatory Leave Request': ['read', 'write', 'create', 'delete'],
'Employee Tax Exemption Declaration': ['read', 'write', 'create', 'delete'],
'Employee Tax Exemption Proof Submission': ['read', 'write', 'create', 'delete'],
'Timesheet': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend']
}
"Employee Self Service": {
"role": "Employee Self Service",
"apply_user_permission_on": "Employee",
"user_id_field": "user_id",
"doctypes": {
"Salary Slip": ["read"],
"Employee": ["read", "write"],
"Expense Claim": ["read", "write", "create", "delete"],
"Leave Application": ["read", "write", "create", "delete"],
"Attendance Request": ["read", "write", "create", "delete"],
"Compensatory Leave Request": ["read", "write", "create", "delete"],
"Employee Tax Exemption Declaration": ["read", "write", "create", "delete"],
"Employee Tax Exemption Proof Submission": ["read", "write", "create", "delete"],
"Timesheet": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
},
}
}
def create_custom_role(data):
if data.get('role') and not frappe.db.exists('Role', data.get('role')):
frappe.get_doc({
'doctype': 'Role',
'role_name': data.get('role'),
'desk_access': 1,
'is_custom': 1
}).insert(ignore_permissions=True)
if data.get("role") and not frappe.db.exists("Role", data.get("role")):
frappe.get_doc(
{"doctype": "Role", "role_name": data.get("role"), "desk_access": 1, "is_custom": 1}
).insert(ignore_permissions=True)
def create_user_type(user_type, data):
if frappe.db.exists('User Type', user_type):
doc = frappe.get_cached_doc('User Type', user_type)
if frappe.db.exists("User Type", user_type):
doc = frappe.get_cached_doc("User Type", user_type)
doc.user_doctypes = []
else:
doc = frappe.new_doc('User Type')
doc.update({
'name': user_type,
'role': data.get('role'),
'user_id_field': data.get('user_id_field'),
'apply_user_permission_on': data.get('apply_user_permission_on')
})
doc = frappe.new_doc("User Type")
doc.update(
{
"name": user_type,
"role": data.get("role"),
"user_id_field": data.get("user_id_field"),
"apply_user_permission_on": data.get("apply_user_permission_on"),
}
)
create_role_permissions_for_doctype(doc, data)
doc.save(ignore_permissions=True)
def create_role_permissions_for_doctype(doc, data):
for doctype, perms in iteritems(data.get('doctypes')):
args = {'document_type': doctype}
for doctype, perms in iteritems(data.get("doctypes")):
args = {"document_type": doctype}
for perm in perms:
args[perm] = 1
doc.append('user_doctypes', args)
doc.append("user_doctypes", args)
def update_select_perm_after_install():
if not frappe.flags.update_select_perm_after_migrate:
return
frappe.flags.ignore_select_perm = False
for row in frappe.get_all('User Type', filters= {'is_standard': 0}):
print('Updating user type :- ', row.name)
doc = frappe.get_doc('User Type', row.name)
for row in frappe.get_all("User Type", filters={"is_standard": 0}):
print("Updating user type :- ", row.name)
doc = frappe.get_doc("User Type", row.name)
doc.save()
frappe.flags.update_select_perm_after_migrate = False

View File

@@ -1,4 +1,3 @@
import json
import frappe
@@ -14,6 +13,7 @@ def get_company_for_dashboards():
return company_list[0].name
return None
def get_default_dashboards():
company = frappe.get_doc("Company", get_company_for_dashboards())
income_account = company.default_income_account or get_account("Income Account", company.name)
@@ -26,20 +26,18 @@ def get_default_dashboards():
"doctype": "Dashboard",
"dashboard_name": "Accounts",
"charts": [
{ "chart": "Outgoing Bills (Sales Invoice)" },
{ "chart": "Incoming Bills (Purchase Invoice)" },
{ "chart": "Bank Balance" },
{ "chart": "Income" },
{ "chart": "Expenses" },
{ "chart": "Patient Appointments" }
]
{"chart": "Outgoing Bills (Sales Invoice)"},
{"chart": "Incoming Bills (Purchase Invoice)"},
{"chart": "Bank Balance"},
{"chart": "Income"},
{"chart": "Expenses"},
{"chart": "Patient Appointments"},
],
},
{
"doctype": "Dashboard",
"dashboard_name": "Project",
"charts": [
{ "chart": "Project Summary", "width": "Full" }
]
"charts": [{"chart": "Project Summary", "width": "Full"}],
},
],
"Charts": [
@@ -55,7 +53,7 @@ def get_default_dashboards():
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half"
"width": "Half",
},
{
"doctype": "Dashboard Chart",
@@ -69,7 +67,7 @@ def get_default_dashboards():
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half"
"width": "Half",
},
{
"doctype": "Dashboard Chart",
@@ -83,7 +81,7 @@ def get_default_dashboards():
"timeseries": 1,
"owner": "Administrator",
"type": "Line",
"width": "Half"
"width": "Half",
},
{
"doctype": "Dashboard Chart",
@@ -99,7 +97,7 @@ def get_default_dashboards():
"owner": "Administrator",
"document_type": "Purchase Invoice",
"type": "Bar",
"width": "Half"
"width": "Half",
},
{
"doctype": "Dashboard Chart",
@@ -115,18 +113,18 @@ def get_default_dashboards():
"owner": "Administrator",
"document_type": "Sales Invoice",
"type": "Bar",
"width": "Half"
"width": "Half",
},
{
'doctype': 'Dashboard Chart',
'name': 'Project Summary',
'chart_name': 'Project Summary',
'chart_type': 'Report',
'report_name': 'Project Summary',
'is_public': 1,
'filters_json': json.dumps({"company": company.name, "status": "Open"}),
'type': 'Bar',
'custom_options': '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}',
"doctype": "Dashboard Chart",
"name": "Project Summary",
"chart_name": "Project Summary",
"chart_type": "Report",
"report_name": "Project Summary",
"is_public": 1,
"filters_json": json.dumps({"company": company.name, "status": "Open"}),
"type": "Bar",
"custom_options": '{"type": "bar", "colors": ["#fc4f51", "#78d6ff", "#7575ff"], "axisOptions": { "shortenYAxisNumbers": 1}, "barOptions": { "stacked": 1 }}',
},
{
"doctype": "Dashboard Chart",
@@ -141,11 +139,12 @@ def get_default_dashboards():
"owner": "Administrator",
"document_type": "Patient Appointment",
"type": "Line",
"width": "Half"
}
]
"width": "Half",
},
],
}
def get_account(account_type, company):
accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company})
if accounts:

View File

@@ -1,58 +1,57 @@
from frappe import _
def get_industry_types():
return [
_('Accounting'),
_('Advertising'),
_('Aerospace'),
_('Agriculture'),
_('Airline'),
_('Apparel & Accessories'),
_('Automotive'),
_('Banking'),
_('Biotechnology'),
_('Broadcasting'),
_('Brokerage'),
_('Chemical'),
_('Computer'),
_('Consulting'),
_('Consumer Products'),
_('Cosmetics'),
_('Defense'),
_('Department Stores'),
_('Education'),
_('Electronics'),
_('Energy'),
_('Entertainment & Leisure'),
_('Executive Search'),
_('Financial Services'),
_('Food, Beverage & Tobacco'),
_('Grocery'),
_('Health Care'),
_('Internet Publishing'),
_('Investment Banking'),
_('Legal'),
_('Manufacturing'),
_('Motion Picture & Video'),
_('Music'),
_('Newspaper Publishers'),
_('Online Auctions'),
_('Pension Funds'),
_('Pharmaceuticals'),
_('Private Equity'),
_('Publishing'),
_('Real Estate'),
_('Retail & Wholesale'),
_('Securities & Commodity Exchanges'),
_('Service'),
_('Soap & Detergent'),
_('Software'),
_('Sports'),
_('Technology'),
_('Telecommunications'),
_('Television'),
_('Transportation'),
_('Venture Capital')
_("Accounting"),
_("Advertising"),
_("Aerospace"),
_("Agriculture"),
_("Airline"),
_("Apparel & Accessories"),
_("Automotive"),
_("Banking"),
_("Biotechnology"),
_("Broadcasting"),
_("Brokerage"),
_("Chemical"),
_("Computer"),
_("Consulting"),
_("Consumer Products"),
_("Cosmetics"),
_("Defense"),
_("Department Stores"),
_("Education"),
_("Electronics"),
_("Energy"),
_("Entertainment & Leisure"),
_("Executive Search"),
_("Financial Services"),
_("Food, Beverage & Tobacco"),
_("Grocery"),
_("Health Care"),
_("Internet Publishing"),
_("Investment Banking"),
_("Legal"),
_("Manufacturing"),
_("Motion Picture & Video"),
_("Music"),
_("Newspaper Publishers"),
_("Online Auctions"),
_("Pension Funds"),
_("Pharmaceuticals"),
_("Private Equity"),
_("Publishing"),
_("Real Estate"),
_("Retail & Wholesale"),
_("Securities & Commodity Exchanges"),
_("Service"),
_("Soap & Detergent"),
_("Software"),
_("Sports"),
_("Technology"),
_("Telecommunications"),
_("Television"),
_("Transportation"),
_("Venture Capital"),
]

View File

@@ -10,41 +10,50 @@ from .default_website import website_maker
def create_fiscal_year_and_company(args):
if (args.get('fy_start_date')):
curr_fiscal_year = get_fy_details(args.get('fy_start_date'), args.get('fy_end_date'))
frappe.get_doc({
"doctype":"Fiscal Year",
'year': curr_fiscal_year,
'year_start_date': args.get('fy_start_date'),
'year_end_date': args.get('fy_end_date'),
}).insert()
if args.get("fy_start_date"):
curr_fiscal_year = get_fy_details(args.get("fy_start_date"), args.get("fy_end_date"))
frappe.get_doc(
{
"doctype": "Fiscal Year",
"year": curr_fiscal_year,
"year_start_date": args.get("fy_start_date"),
"year_end_date": args.get("fy_end_date"),
}
).insert()
if args.get("company_name"):
frappe.get_doc(
{
"doctype": "Company",
"company_name": args.get("company_name"),
"enable_perpetual_inventory": 1,
"abbr": args.get("company_abbr"),
"default_currency": args.get("currency"),
"country": args.get("country"),
"create_chart_of_accounts_based_on": "Standard Template",
"chart_of_accounts": args.get("chart_of_accounts"),
"domain": args.get("domains")[0],
}
).insert()
if (args.get('company_name')):
frappe.get_doc({
"doctype":"Company",
'company_name':args.get('company_name'),
'enable_perpetual_inventory': 1,
'abbr':args.get('company_abbr'),
'default_currency':args.get('currency'),
'country': args.get('country'),
'create_chart_of_accounts_based_on': 'Standard Template',
'chart_of_accounts': args.get('chart_of_accounts'),
'domain': args.get('domains')[0]
}).insert()
def enable_shopping_cart(args):
# Needs price_lists
frappe.get_doc({
"doctype": "E Commerce Settings",
"enabled": 1,
'company': args.get('company_name') ,
'price_list': frappe.db.get_value("Price List", {"selling": 1}),
'default_customer_group': _("Individual"),
'quotation_series': "QTN-",
}).insert()
frappe.get_doc(
{
"doctype": "E Commerce Settings",
"enabled": 1,
"company": args.get("company_name"),
"price_list": frappe.db.get_value("Price List", {"selling": 1}),
"default_customer_group": _("Individual"),
"quotation_series": "QTN-",
}
).insert()
def create_email_digest():
from frappe.utils.user import get_system_managers
system_managers = get_system_managers(only_name=True)
if not system_managers:
@@ -52,20 +61,20 @@ def create_email_digest():
recipients = []
for d in system_managers:
recipients.append({
'recipient': d
})
recipients.append({"recipient": d})
companies = frappe.db.sql_list("select name FROM `tabCompany`")
for company in companies:
if not frappe.db.exists("Email Digest", "Default Weekly Digest - " + company):
edigest = frappe.get_doc({
"doctype": "Email Digest",
"name": "Default Weekly Digest - " + company,
"company": company,
"frequency": "Weekly",
"recipients": recipients
})
edigest = frappe.get_doc(
{
"doctype": "Email Digest",
"name": "Default Weekly Digest - " + company,
"company": company,
"frequency": "Weekly",
"recipients": recipients,
}
)
for df in edigest.meta.get("fields", {"fieldtype": "Check"}):
if df.fieldname != "scheduler_errors":
@@ -76,39 +85,53 @@ def create_email_digest():
# scheduler errors digest
if companies:
edigest = frappe.new_doc("Email Digest")
edigest.update({
"name": "Scheduler Errors",
"company": companies[0],
"frequency": "Daily",
"recipients": recipients,
"scheduler_errors": 1,
"enabled": 1
})
edigest.update(
{
"name": "Scheduler Errors",
"company": companies[0],
"frequency": "Daily",
"recipients": recipients,
"scheduler_errors": 1,
"enabled": 1,
}
)
edigest.insert()
def create_logo(args):
if args.get("attach_logo"):
attach_logo = args.get("attach_logo").split(",")
if len(attach_logo)==3:
if len(attach_logo) == 3:
filename, filetype, content = attach_logo
_file = frappe.get_doc({
"doctype": "File",
"file_name": filename,
"attached_to_doctype": "Website Settings",
"attached_to_name": "Website Settings",
"decode": True})
_file = frappe.get_doc(
{
"doctype": "File",
"file_name": filename,
"attached_to_doctype": "Website Settings",
"attached_to_name": "Website Settings",
"decode": True,
}
)
_file.save()
fileurl = _file.file_url
frappe.db.set_value("Website Settings", "Website Settings", "brand_html",
"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(fileurl, args.get("company_name")))
frappe.db.set_value(
"Website Settings",
"Website Settings",
"brand_html",
"<img src='{0}' style='max-width: 40px; max-height: 25px;'> {1}".format(
fileurl, args.get("company_name")
),
)
def create_website(args):
website_maker(args)
def get_fy_details(fy_start_date, fy_end_date):
start_year = getdate(fy_start_date).year
if start_year == getdate(fy_end_date).year:
fy = cstr(start_year)
else:
fy = cstr(start_year) + '-' + cstr(start_year + 1)
fy = cstr(start_year) + "-" + cstr(start_year + 1)
return fy

View File

@@ -12,14 +12,14 @@ class website_maker(object):
self.args = args
self.company = args.company_name
self.tagline = args.company_tagline
self.user = args.get('email')
self.user = args.get("email")
self.make_web_page()
self.make_website_settings()
self.make_blog()
def make_web_page(self):
# home page
homepage = frappe.get_doc('Homepage', 'Homepage')
homepage = frappe.get_doc("Homepage", "Homepage")
homepage.company = self.company
homepage.tag_line = self.tagline
homepage.setup_items()
@@ -28,34 +28,25 @@ class website_maker(object):
def make_website_settings(self):
# update in home page in settings
website_settings = frappe.get_doc("Website Settings", "Website Settings")
website_settings.home_page = 'home'
website_settings.home_page = "home"
website_settings.brand_html = self.company
website_settings.copyright = self.company
website_settings.top_bar_items = []
website_settings.append("top_bar_items", {
"doctype": "Top Bar Item",
"label":"Contact",
"url": "/contact"
})
website_settings.append("top_bar_items", {
"doctype": "Top Bar Item",
"label":"Blog",
"url": "/blog"
})
website_settings.append("top_bar_items", {
"doctype": "Top Bar Item",
"label": _("Products"),
"url": "/all-products"
})
website_settings.append(
"top_bar_items", {"doctype": "Top Bar Item", "label": "Contact", "url": "/contact"}
)
website_settings.append(
"top_bar_items", {"doctype": "Top Bar Item", "label": "Blog", "url": "/blog"}
)
website_settings.append(
"top_bar_items", {"doctype": "Top Bar Item", "label": _("Products"), "url": "/all-products"}
)
website_settings.save()
def make_blog(self):
blog_category = frappe.get_doc({
"doctype": "Blog Category",
"category_name": "general",
"published": 1,
"title": _("General")
}).insert()
blog_category = frappe.get_doc(
{"doctype": "Blog Category", "category_name": "general", "published": 1, "title": _("General")}
).insert()
if not self.user:
# Admin setup
@@ -69,21 +60,30 @@ class website_maker(object):
blogger.avatar = user.user_image
blogger.insert()
frappe.get_doc({
"doctype": "Blog Post",
"title": "Welcome",
"published": 1,
"published_on": nowdate(),
"blogger": blogger.name,
"blog_category": blog_category.name,
"blog_intro": "My First Blog",
"content": frappe.get_template("setup/setup_wizard/data/sample_blog_post.html").render(),
}).insert()
frappe.get_doc(
{
"doctype": "Blog Post",
"title": "Welcome",
"published": 1,
"published_on": nowdate(),
"blogger": blogger.name,
"blog_category": blog_category.name,
"blog_intro": "My First Blog",
"content": frappe.get_template("setup/setup_wizard/data/sample_blog_post.html").render(),
}
).insert()
def test():
frappe.delete_doc("Web Page", "test-company")
frappe.delete_doc("Blog Post", "welcome")
frappe.delete_doc("Blogger", "administrator")
frappe.delete_doc("Blog Category", "general")
website_maker({'company':"Test Company", 'company_tagline': "Better Tools for Everyone", 'name': "Administrator"})
website_maker(
{
"company": "Test Company",
"company_tagline": "Better Tools for Everyone",
"name": "Administrator",
}
)
frappe.db.commit()

View File

@@ -12,12 +12,14 @@ def set_default_settings(args):
frappe.db.set_value("Currency", args.get("currency"), "enabled", 1)
global_defaults = frappe.get_doc("Global Defaults", "Global Defaults")
global_defaults.update({
'current_fiscal_year': get_fy_details(args.get('fy_start_date'), args.get('fy_end_date')),
'default_currency': args.get('currency'),
'default_company':args.get('company_name') ,
"country": args.get("country"),
})
global_defaults.update(
{
"current_fiscal_year": get_fy_details(args.get("fy_start_date"), args.get("fy_end_date")),
"default_currency": args.get("currency"),
"default_company": args.get("company_name"),
"country": args.get("country"),
}
)
global_defaults.save()
@@ -25,13 +27,15 @@ def set_default_settings(args):
system_settings.email_footer_address = args.get("company_name")
system_settings.save()
domain_settings = frappe.get_single('Domain Settings')
domain_settings.set_active_domains(args.get('domains'))
domain_settings = frappe.get_single("Domain Settings")
domain_settings.set_active_domains(args.get("domains"))
stock_settings = frappe.get_doc("Stock Settings")
stock_settings.item_naming_by = "Item Code"
stock_settings.valuation_method = "FIFO"
stock_settings.default_warehouse = frappe.db.get_value('Warehouse', {'warehouse_name': _('Stores')})
stock_settings.default_warehouse = frappe.db.get_value(
"Warehouse", {"warehouse_name": _("Stores")}
)
stock_settings.stock_uom = _("Nos")
stock_settings.auto_indent = 1
stock_settings.auto_insert_price_list_rate_if_missing = 1
@@ -72,61 +76,74 @@ def set_default_settings(args):
hr_settings.feedback_reminder_notification_template = _("Interview Feedback Reminder")
hr_settings.save()
def set_no_copy_fields_in_variant_settings():
# set no copy fields of an item doctype to item variant settings
doc = frappe.get_doc('Item Variant Settings')
doc = frappe.get_doc("Item Variant Settings")
doc.set_default_fields()
doc.save()
def create_price_lists(args):
for pl_type, pl_name in (("Selling", _("Standard Selling")), ("Buying", _("Standard Buying"))):
frappe.get_doc({
"doctype": "Price List",
"price_list_name": pl_name,
"enabled": 1,
"buying": 1 if pl_type == "Buying" else 0,
"selling": 1 if pl_type == "Selling" else 0,
"currency": args["currency"]
}).insert()
frappe.get_doc(
{
"doctype": "Price List",
"price_list_name": pl_name,
"enabled": 1,
"buying": 1 if pl_type == "Buying" else 0,
"selling": 1 if pl_type == "Selling" else 0,
"currency": args["currency"],
}
).insert()
def create_employee_for_self(args):
if frappe.session.user == 'Administrator':
if frappe.session.user == "Administrator":
return
# create employee for self
emp = frappe.get_doc({
"doctype": "Employee",
"employee_name": " ".join(filter(None, [args.get("first_name"), args.get("last_name")])),
"user_id": frappe.session.user,
"status": "Active",
"company": args.get("company_name")
})
emp = frappe.get_doc(
{
"doctype": "Employee",
"employee_name": " ".join(filter(None, [args.get("first_name"), args.get("last_name")])),
"user_id": frappe.session.user,
"status": "Active",
"company": args.get("company_name"),
}
)
emp.flags.ignore_mandatory = True
emp.insert(ignore_permissions = True)
emp.insert(ignore_permissions=True)
def create_territories():
"""create two default territories, one for home country and one named Rest of the World"""
from frappe.utils.nestedset import get_root_of
country = frappe.db.get_default("country")
root_territory = get_root_of("Territory")
for name in (country, _("Rest Of The World")):
if name and not frappe.db.exists("Territory", name):
frappe.get_doc({
"doctype": "Territory",
"territory_name": name.replace("'", ""),
"parent_territory": root_territory,
"is_group": "No"
}).insert()
frappe.get_doc(
{
"doctype": "Territory",
"territory_name": name.replace("'", ""),
"parent_territory": root_territory,
"is_group": "No",
}
).insert()
def create_feed_and_todo():
"""update Activity feed and create todo for creation of item, customer, vendor"""
return
def get_fy_details(fy_start_date, fy_end_date):
start_year = getdate(fy_start_date).year
if start_year == getdate(fy_end_date).year:
fy = cstr(start_year)
else:
fy = cstr(start_year) + '-' + cstr(start_year + 1)
fy = cstr(start_year) + "-" + cstr(start_year + 1)
return fy

File diff suppressed because it is too large Load Diff

View File

@@ -12,12 +12,12 @@ from frappe import _
from frappe.utils.make_random import add_random_children
def make_sample_data(domains, make_dependent = False):
def make_sample_data(domains, make_dependent=False):
"""Create a few opportunities, quotes, material requests, issues, todos, projects
to help the user get started"""
if make_dependent:
items = frappe.get_all("Item", {'is_sales_item': 1})
items = frappe.get_all("Item", {"is_sales_item": 1})
customers = frappe.get_all("Customer")
warehouses = frappe.get_all("Warehouse")
@@ -33,91 +33,109 @@ def make_sample_data(domains, make_dependent = False):
make_projects(domains)
import_notification()
def make_opportunity(items, customer):
b = frappe.get_doc({
"doctype": "Opportunity",
"opportunity_from": "Customer",
"customer": customer,
"opportunity_type": _("Sales"),
"with_items": 1
})
add_random_children(b, "items", rows=len(items), randomize = {
"qty": (1, 5),
"item_code": ["Item"]
}, unique="item_code")
def make_opportunity(items, customer):
b = frappe.get_doc(
{
"doctype": "Opportunity",
"opportunity_from": "Customer",
"customer": customer,
"opportunity_type": _("Sales"),
"with_items": 1,
}
)
add_random_children(
b, "items", rows=len(items), randomize={"qty": (1, 5), "item_code": ["Item"]}, unique="item_code"
)
b.insert(ignore_permissions=True)
b.add_comment('Comment', text="This is a dummy record")
b.add_comment("Comment", text="This is a dummy record")
def make_quote(items, customer):
qtn = frappe.get_doc({
"doctype": "Quotation",
"quotation_to": "Customer",
"party_name": customer,
"order_type": "Sales"
})
qtn = frappe.get_doc(
{
"doctype": "Quotation",
"quotation_to": "Customer",
"party_name": customer,
"order_type": "Sales",
}
)
add_random_children(qtn, "items", rows=len(items), randomize = {
"qty": (1, 5),
"item_code": ["Item"]
}, unique="item_code")
add_random_children(
qtn,
"items",
rows=len(items),
randomize={"qty": (1, 5), "item_code": ["Item"]},
unique="item_code",
)
qtn.insert(ignore_permissions=True)
qtn.add_comment('Comment', text="This is a dummy record")
qtn.add_comment("Comment", text="This is a dummy record")
def make_material_request(items):
for i in items:
mr = frappe.get_doc({
"doctype": "Material Request",
"material_request_type": "Purchase",
"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
"items": [{
mr = frappe.get_doc(
{
"doctype": "Material Request",
"material_request_type": "Purchase",
"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
"item_code": i.name,
"qty": 10
}]
})
"items": [
{
"schedule_date": frappe.utils.add_days(frappe.utils.nowdate(), 7),
"item_code": i.name,
"qty": 10,
}
],
}
)
mr.insert()
mr.submit()
mr.add_comment('Comment', text="This is a dummy record")
mr.add_comment("Comment", text="This is a dummy record")
def make_issue():
pass
def make_projects(domains):
current_date = frappe.utils.nowdate()
project = frappe.get_doc({
"doctype": "Project",
"project_name": "ERPNext Implementation",
})
project = frappe.get_doc(
{
"doctype": "Project",
"project_name": "ERPNext Implementation",
}
)
tasks = [
{
"title": "Explore ERPNext",
"start_date": current_date,
"end_date": current_date,
"file": "explore.md"
}]
"file": "explore.md",
}
]
if 'Education' in domains:
if "Education" in domains:
tasks += [
{
"title": _("Setup your Institute in ERPNext"),
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 1),
"file": "education_masters.md"
"file": "education_masters.md",
},
{
"title": "Setup Master Data",
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 1),
"file": "education_masters.md"
}]
"file": "education_masters.md",
},
]
else:
tasks += [
@@ -125,55 +143,59 @@ def make_projects(domains):
"title": "Setup Your Company",
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 1),
"file": "masters.md"
"file": "masters.md",
},
{
"title": "Start Tracking your Sales",
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 2),
"file": "sales.md"
"file": "sales.md",
},
{
"title": "Start Managing Purchases",
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 3),
"file": "purchase.md"
"file": "purchase.md",
},
{
"title": "Import Data",
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 4),
"file": "import_data.md"
"file": "import_data.md",
},
{
"title": "Go Live!",
"start_date": current_date,
"end_date": frappe.utils.add_days(current_date, 5),
"file": "go_live.md"
}]
"file": "go_live.md",
},
]
for t in tasks:
with open (os.path.join(os.path.dirname(__file__), "tasks", t['file'])) as f:
t['description'] = frappe.utils.md_to_html(f.read())
del t['file']
with open(os.path.join(os.path.dirname(__file__), "tasks", t["file"])) as f:
t["description"] = frappe.utils.md_to_html(f.read())
del t["file"]
project.append('tasks', t)
project.append("tasks", t)
project.insert(ignore_permissions=True)
def import_notification():
'''Import notification for task start'''
with open (os.path.join(os.path.dirname(__file__), "tasks/task_alert.json")) as f:
"""Import notification for task start"""
with open(os.path.join(os.path.dirname(__file__), "tasks/task_alert.json")) as f:
notification = frappe.get_doc(json.loads(f.read())[0])
notification.insert()
# trigger the first message!
from frappe.email.doctype.notification.notification import trigger_daily_alerts
trigger_daily_alerts()
def test_sample():
frappe.db.sql('delete from `tabNotification`')
frappe.db.sql('delete from tabProject')
frappe.db.sql('delete from tabTask')
make_projects('Education')
frappe.db.sql("delete from `tabNotification`")
frappe.db.sql("delete from tabProject")
frappe.db.sql("delete from tabTask")
make_projects("Education")
import_notification()

View File

@@ -10,11 +10,11 @@ from frappe import _
def setup_taxes_and_charges(company_name: str, country: str):
if not frappe.db.exists('Company', company_name):
frappe.throw(_('Company {} does not exist yet. Taxes setup aborted.').format(company_name))
if not frappe.db.exists("Company", company_name):
frappe.throw(_("Company {} does not exist yet. Taxes setup aborted.").format(company_name))
file_path = os.path.join(os.path.dirname(__file__), '..', 'data', 'country_wise_tax.json')
with open(file_path, 'r') as json_file:
file_path = os.path.join(os.path.dirname(__file__), "..", "data", "country_wise_tax.json")
with open(file_path, "r") as json_file:
tax_data = json.load(json_file)
country_wise_tax = tax_data.get(country)
@@ -22,7 +22,7 @@ def setup_taxes_and_charges(company_name: str, country: str):
if not country_wise_tax:
return
if 'chart_of_accounts' not in country_wise_tax:
if "chart_of_accounts" not in country_wise_tax:
country_wise_tax = simple_to_detailed(country_wise_tax)
from_detailed_data(company_name, country_wise_tax)
@@ -36,39 +36,44 @@ def simple_to_detailed(templates):
Example input:
{
"France VAT 20%": {
"account_name": "VAT 20%",
"tax_rate": 20,
"default": 1
},
"France VAT 10%": {
"account_name": "VAT 10%",
"tax_rate": 10
}
"France VAT 20%": {
"account_name": "VAT 20%",
"tax_rate": 20,
"default": 1
},
"France VAT 10%": {
"account_name": "VAT 10%",
"tax_rate": 10
}
}
"""
return {
'chart_of_accounts': {
'*': {
'item_tax_templates': [{
'title': title,
'taxes': [{
'tax_type': {
'account_name': data.get('account_name'),
'tax_rate': data.get('tax_rate')
}
}]
} for title, data in templates.items()],
'*': [{
'title': title,
'is_default': data.get('default', 0),
'taxes': [{
'account_head': {
'account_name': data.get('account_name'),
'tax_rate': data.get('tax_rate')
}
}]
} for title, data in templates.items()]
"chart_of_accounts": {
"*": {
"item_tax_templates": [
{
"title": title,
"taxes": [
{"tax_type": {"account_name": data.get("account_name"), "tax_rate": data.get("tax_rate")}}
],
}
for title, data in templates.items()
],
"*": [
{
"title": title,
"is_default": data.get("default", 0),
"taxes": [
{
"account_head": {
"account_name": data.get("account_name"),
"tax_rate": data.get("tax_rate"),
}
}
],
}
for title, data in templates.items()
],
}
}
}
@@ -76,13 +81,13 @@ def simple_to_detailed(templates):
def from_detailed_data(company_name, data):
"""Create Taxes and Charges Templates from detailed data."""
coa_name = frappe.db.get_value('Company', company_name, 'chart_of_accounts')
coa_data = data.get('chart_of_accounts', {})
tax_templates = coa_data.get(coa_name) or coa_data.get('*', {})
tax_categories = data.get('tax_categories')
sales_tax_templates = tax_templates.get('sales_tax_templates') or tax_templates.get('*', {})
purchase_tax_templates = tax_templates.get('purchase_tax_templates') or tax_templates.get('*', {})
item_tax_templates = tax_templates.get('item_tax_templates') or tax_templates.get('*', {})
coa_name = frappe.db.get_value("Company", company_name, "chart_of_accounts")
coa_data = data.get("chart_of_accounts", {})
tax_templates = coa_data.get(coa_name) or coa_data.get("*", {})
tax_categories = data.get("tax_categories")
sales_tax_templates = tax_templates.get("sales_tax_templates") or tax_templates.get("*", {})
purchase_tax_templates = tax_templates.get("purchase_tax_templates") or tax_templates.get("*", {})
item_tax_templates = tax_templates.get("item_tax_templates") or tax_templates.get("*", {})
if tax_categories:
for tax_category in tax_categories:
@@ -90,11 +95,11 @@ def from_detailed_data(company_name, data):
if sales_tax_templates:
for template in sales_tax_templates:
make_taxes_and_charges_template(company_name, 'Sales Taxes and Charges Template', template)
make_taxes_and_charges_template(company_name, "Sales Taxes and Charges Template", template)
if purchase_tax_templates:
for template in purchase_tax_templates:
make_taxes_and_charges_template(company_name, 'Purchase Taxes and Charges Template', template)
make_taxes_and_charges_template(company_name, "Purchase Taxes and Charges Template", template)
if item_tax_templates:
for template in item_tax_templates:
@@ -102,40 +107,45 @@ def from_detailed_data(company_name, data):
def update_regional_tax_settings(country, company):
path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(country))
path = frappe.get_app_path("erpnext", "regional", frappe.scrub(country))
if os.path.exists(path.encode("utf-8")):
try:
module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(frappe.scrub(country))
module_name = "erpnext.regional.{0}.setup.update_regional_tax_settings".format(
frappe.scrub(country)
)
frappe.get_attr(module_name)(country, company)
except Exception as e:
# Log error and ignore if failed to setup regional tax settings
frappe.log_error()
pass
def make_taxes_and_charges_template(company_name, doctype, template):
template['company'] = company_name
template['doctype'] = doctype
if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
def make_taxes_and_charges_template(company_name, doctype, template):
template["company"] = company_name
template["doctype"] = doctype
if frappe.db.exists(doctype, {"title": template.get("title"), "company": company_name}):
return
for tax_row in template.get('taxes'):
account_data = tax_row.get('account_head')
for tax_row in template.get("taxes"):
account_data = tax_row.get("account_head")
tax_row_defaults = {
'category': 'Total',
'charge_type': 'On Net Total',
'cost_center': frappe.db.get_value('Company', company_name, 'cost_center')
"category": "Total",
"charge_type": "On Net Total",
"cost_center": frappe.db.get_value("Company", company_name, "cost_center"),
}
if doctype == 'Purchase Taxes and Charges Template':
tax_row_defaults['add_deduct_tax'] = 'Add'
if doctype == "Purchase Taxes and Charges Template":
tax_row_defaults["add_deduct_tax"] = "Add"
# if account_head is a dict, search or create the account and get it's name
if isinstance(account_data, dict):
tax_row_defaults['description'] = '{0} @ {1}'.format(account_data.get('account_name'), account_data.get('tax_rate'))
tax_row_defaults['rate'] = account_data.get('tax_rate')
tax_row_defaults["description"] = "{0} @ {1}".format(
account_data.get("account_name"), account_data.get("tax_rate")
)
tax_row_defaults["rate"] = account_data.get("tax_rate")
account = get_or_create_account(company_name, account_data)
tax_row['account_head'] = account.name
tax_row["account_head"] = account.name
# use the default value if nothing other is specified
for fieldname, default_value in tax_row_defaults.items():
@@ -151,28 +161,29 @@ def make_taxes_and_charges_template(company_name, doctype, template):
doc.insert(ignore_permissions=True)
return doc
def make_item_tax_template(company_name, template):
"""Create an Item Tax Template.
This requires a separate method because Item Tax Template is structured
differently from Sales and Purchase Tax Templates.
"""
doctype = 'Item Tax Template'
template['company'] = company_name
template['doctype'] = doctype
doctype = "Item Tax Template"
template["company"] = company_name
template["doctype"] = doctype
if frappe.db.exists(doctype, {'title': template.get('title'), 'company': company_name}):
if frappe.db.exists(doctype, {"title": template.get("title"), "company": company_name}):
return
for tax_row in template.get('taxes'):
account_data = tax_row.get('tax_type')
for tax_row in template.get("taxes"):
account_data = tax_row.get("tax_type")
# if tax_type is a dict, search or create the account and get it's name
if isinstance(account_data, dict):
account = get_or_create_account(company_name, account_data)
tax_row['tax_type'] = account.name
if 'tax_rate' not in tax_row:
tax_row['tax_rate'] = account_data.get('tax_rate')
tax_row["tax_type"] = account.name
if "tax_rate" not in tax_row:
tax_row["tax_rate"] = account_data.get("tax_rate")
doc = frappe.get_doc(template)
@@ -183,46 +194,47 @@ def make_item_tax_template(company_name, template):
doc.insert(ignore_permissions=True)
return doc
def make_tax_category(tax_category):
""" Make tax category based on title if not already created """
doctype = 'Tax Category'
if not frappe.db.exists(doctype, tax_category['title']):
tax_category['doctype'] = doctype
"""Make tax category based on title if not already created"""
doctype = "Tax Category"
if not frappe.db.exists(doctype, tax_category["title"]):
tax_category["doctype"] = doctype
doc = frappe.get_doc(tax_category)
doc.flags.ignore_links = True
doc.flags.ignore_validate = True
doc.insert(ignore_permissions=True)
def get_or_create_account(company_name, account):
"""
Check if account already exists. If not, create it.
Return a tax account or None.
"""
default_root_type = 'Liability'
root_type = account.get('root_type', default_root_type)
default_root_type = "Liability"
root_type = account.get("root_type", default_root_type)
existing_accounts = frappe.get_all('Account',
filters={
'company': company_name,
'root_type': root_type
},
existing_accounts = frappe.get_all(
"Account",
filters={"company": company_name, "root_type": root_type},
or_filters={
'account_name': account.get('account_name'),
'account_number': account.get('account_number')
})
"account_name": account.get("account_name"),
"account_number": account.get("account_number"),
},
)
if existing_accounts:
return frappe.get_doc('Account', existing_accounts[0].name)
return frappe.get_doc("Account", existing_accounts[0].name)
tax_group = get_or_create_tax_group(company_name, root_type)
account['doctype'] = 'Account'
account['company'] = company_name
account['parent_account'] = tax_group
account['report_type'] = 'Balance Sheet'
account['account_type'] = 'Tax'
account['root_type'] = root_type
account['is_group'] = 0
account["doctype"] = "Account"
account["company"] = company_name
account["parent_account"] = tax_group
account["report_type"] = "Balance Sheet"
account["account_type"] = "Tax"
account["root_type"] = root_type
account["is_group"] = 0
doc = frappe.get_doc(account)
doc.flags.ignore_links = True
@@ -230,50 +242,53 @@ def get_or_create_account(company_name, account):
doc.insert(ignore_permissions=True, ignore_mandatory=True)
return doc
def get_or_create_tax_group(company_name, root_type):
# Look for a group account of type 'Tax'
tax_group_name = frappe.db.get_value('Account', {
'is_group': 1,
'root_type': root_type,
'account_type': 'Tax',
'company': company_name
})
tax_group_name = frappe.db.get_value(
"Account",
{"is_group": 1, "root_type": root_type, "account_type": "Tax", "company": company_name},
)
if tax_group_name:
return tax_group_name
# Look for a group account named 'Duties and Taxes' or 'Tax Assets'
account_name = _('Duties and Taxes') if root_type == 'Liability' else _('Tax Assets')
tax_group_name = frappe.db.get_value('Account', {
'is_group': 1,
'root_type': root_type,
'account_name': account_name,
'company': company_name
})
account_name = _("Duties and Taxes") if root_type == "Liability" else _("Tax Assets")
tax_group_name = frappe.db.get_value(
"Account",
{"is_group": 1, "root_type": root_type, "account_name": account_name, "company": company_name},
)
if tax_group_name:
return tax_group_name
# Create a new group account named 'Duties and Taxes' or 'Tax Assets' just
# below the root account
root_account = frappe.get_all('Account', {
'is_group': 1,
'root_type': root_type,
'company': company_name,
'report_type': 'Balance Sheet',
'parent_account': ('is', 'not set')
}, limit=1)[0]
root_account = frappe.get_all(
"Account",
{
"is_group": 1,
"root_type": root_type,
"company": company_name,
"report_type": "Balance Sheet",
"parent_account": ("is", "not set"),
},
limit=1,
)[0]
tax_group_account = frappe.get_doc({
'doctype': 'Account',
'company': company_name,
'is_group': 1,
'report_type': 'Balance Sheet',
'root_type': root_type,
'account_type': 'Tax',
'account_name': account_name,
'parent_account': root_account.name
})
tax_group_account = frappe.get_doc(
{
"doctype": "Account",
"company": company_name,
"is_group": 1,
"report_type": "Balance Sheet",
"root_type": root_type,
"account_type": "Tax",
"account_name": account_name,
"parent_account": root_account.name,
}
)
tax_group_account.flags.ignore_links = True
tax_group_account.flags.ignore_validate = True
@@ -285,11 +300,11 @@ def get_or_create_tax_group(company_name, root_type):
def make_tax_catgory(tax_category):
doctype = 'Tax Category'
doctype = "Tax Category"
if isinstance(tax_category, str):
tax_category = {'title': tax_category}
tax_category = {"title": tax_category}
tax_category['doctype'] = doctype
if not frappe.db.exists(doctype, tax_category['title']):
tax_category["doctype"] = doctype
if not frappe.db.exists(doctype, tax_category["title"]):
doc = frappe.get_doc(tax_category)
doc.insert(ignore_permissions=True)

View File

@@ -14,96 +14,66 @@ def get_setup_stages(args=None):
if frappe.db.sql("select name from tabCompany"):
stages = [
{
'status': _('Wrapping up'),
'fail_msg': _('Failed to login'),
'tasks': [
{
'fn': fin,
'args': args,
'fail_msg': _("Failed to login")
}
]
"status": _("Wrapping up"),
"fail_msg": _("Failed to login"),
"tasks": [{"fn": fin, "args": args, "fail_msg": _("Failed to login")}],
}
]
else:
stages = [
{
'status': _('Installing presets'),
'fail_msg': _('Failed to install presets'),
'tasks': [
{
'fn': stage_fixtures,
'args': args,
'fail_msg': _("Failed to install presets")
}
]
"status": _("Installing presets"),
"fail_msg": _("Failed to install presets"),
"tasks": [{"fn": stage_fixtures, "args": args, "fail_msg": _("Failed to install presets")}],
},
{
'status': _('Setting up company'),
'fail_msg': _('Failed to setup company'),
'tasks': [
{
'fn': setup_company,
'args': args,
'fail_msg': _("Failed to setup company")
}
]
"status": _("Setting up company"),
"fail_msg": _("Failed to setup company"),
"tasks": [{"fn": setup_company, "args": args, "fail_msg": _("Failed to setup company")}],
},
{
'status': _('Setting defaults'),
'fail_msg': 'Failed to set defaults',
'tasks': [
{
'fn': setup_defaults,
'args': args,
'fail_msg': _("Failed to setup defaults")
},
{
'fn': stage_four,
'args': args,
'fail_msg': _("Failed to create website")
},
{
'fn': set_active_domains,
'args': args,
'fail_msg': _("Failed to add Domain")
},
]
"status": _("Setting defaults"),
"fail_msg": "Failed to set defaults",
"tasks": [
{"fn": setup_defaults, "args": args, "fail_msg": _("Failed to setup defaults")},
{"fn": stage_four, "args": args, "fail_msg": _("Failed to create website")},
{"fn": set_active_domains, "args": args, "fail_msg": _("Failed to add Domain")},
],
},
{
'status': _('Wrapping up'),
'fail_msg': _('Failed to login'),
'tasks': [
{
'fn': fin,
'args': args,
'fail_msg': _("Failed to login")
}
]
}
"status": _("Wrapping up"),
"fail_msg": _("Failed to login"),
"tasks": [{"fn": fin, "args": args, "fail_msg": _("Failed to login")}],
},
]
return stages
def stage_fixtures(args):
fixtures.install(args.get('country'))
fixtures.install(args.get("country"))
def setup_company(args):
fixtures.install_company(args)
def setup_defaults(args):
fixtures.install_defaults(frappe._dict(args))
def stage_four(args):
company_setup.create_website(args)
company_setup.create_email_digest()
company_setup.create_logo(args)
def fin(args):
frappe.local.message_log = []
login_as_first_user(args)
make_sample_data(args.get('domains'))
make_sample_data(args.get("domains"))
def make_sample_data(domains):
try:
@@ -114,6 +84,7 @@ def make_sample_data(domains):
frappe.message_log.pop()
pass
def login_as_first_user(args):
if args.get("email") and hasattr(frappe.local, "login_manager"):
frappe.local.login_manager.login_as(args.get("email"))
@@ -127,6 +98,7 @@ def setup_complete(args=None):
stage_four(args)
fin(args)
def set_active_domains(args):
domain_settings = frappe.get_single('Domain Settings')
domain_settings.set_active_domains(args.get('domains'))
domain_settings = frappe.get_single("Domain Settings")
domain_settings.set_active_domains(args.get("domains"))

View File

@@ -1,4 +1,3 @@
import json
import os
@@ -6,8 +5,7 @@ from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
def complete():
with open(os.path.join(os.path.dirname(__file__),
'data', 'test_mfg.json'), 'r') as f:
with open(os.path.join(os.path.dirname(__file__), "data", "test_mfg.json"), "r") as f:
data = json.loads(f.read())
setup_complete(data)

View File

@@ -11,40 +11,51 @@ from erpnext import get_default_company
def get_root_of(doctype):
"""Get root element of a DocType with a tree structure"""
result = frappe.db.sql_list("""select name from `tab%s`
where lft=1 and rgt=(select max(rgt) from `tab%s` where docstatus < 2)""" %
(doctype, doctype))
result = frappe.db.sql_list(
"""select name from `tab%s`
where lft=1 and rgt=(select max(rgt) from `tab%s` where docstatus < 2)"""
% (doctype, doctype)
)
return result[0] if result else None
def get_ancestors_of(doctype, name):
"""Get ancestor elements of a DocType with a tree structure"""
lft, rgt = frappe.db.get_value(doctype, name, ["lft", "rgt"])
result = frappe.db.sql_list("""select name from `tab%s`
where lft<%s and rgt>%s order by lft desc""" % (doctype, "%s", "%s"), (lft, rgt))
result = frappe.db.sql_list(
"""select name from `tab%s`
where lft<%s and rgt>%s order by lft desc"""
% (doctype, "%s", "%s"),
(lft, rgt),
)
return result or []
def before_tests():
frappe.clear_cache()
# complete setup if missing
from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
if not frappe.get_list("Company"):
setup_complete({
"currency" :"USD",
"full_name" :"Test User",
"company_name" :"Wind Power LLC",
"timezone" :"America/New_York",
"company_abbr" :"WP",
"industry" :"Manufacturing",
"country" :"United States",
"fy_start_date" :"2011-01-01",
"fy_end_date" :"2011-12-31",
"language" :"english",
"company_tagline" :"Testing",
"email" :"test@erpnext.com",
"password" :"test",
"chart_of_accounts" : "Standard",
"domains" : ["Manufacturing"],
})
setup_complete(
{
"currency": "USD",
"full_name": "Test User",
"company_name": "Wind Power LLC",
"timezone": "America/New_York",
"company_abbr": "WP",
"industry": "Manufacturing",
"country": "United States",
"fy_start_date": "2011-01-01",
"fy_end_date": "2011-12-31",
"language": "english",
"company_tagline": "Testing",
"email": "test@erpnext.com",
"password": "test",
"chart_of_accounts": "Standard",
"domains": ["Manufacturing"],
}
)
frappe.db.sql("delete from `tabLeave Allocation`")
frappe.db.sql("delete from `tabLeave Application`")
@@ -57,6 +68,7 @@ def before_tests():
frappe.db.commit()
@frappe.whitelist()
def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=None):
if not (from_currency and to_currency):
@@ -73,7 +85,7 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
filters = [
["date", "<=", get_datetime_str(transaction_date)],
["from_currency", "=", from_currency],
["to_currency", "=", to_currency]
["to_currency", "=", to_currency],
]
if args == "for_buying":
@@ -88,8 +100,8 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
# cksgb 19/09/2016: get last entry in Currency Exchange with from_currency and to_currency.
entries = frappe.get_all(
"Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc",
limit=1)
"Currency Exchange", fields=["exchange_rate"], filters=filters, order_by="date desc", limit=1
)
if entries:
return flt(entries[0].exchange_rate)
@@ -100,12 +112,11 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
if not value:
import requests
api_url = "https://api.exchangerate.host/convert"
response = requests.get(api_url, params={
"date": transaction_date,
"from": from_currency,
"to": to_currency
})
response = requests.get(
api_url, params={"date": transaction_date, "from": from_currency, "to": to_currency}
)
# expire in 6 hours
response.raise_for_status()
value = response.json()["result"]
@@ -113,20 +124,26 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
return flt(value)
except Exception:
frappe.log_error(title="Get Exchange Rate")
frappe.msgprint(_("Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually").format(from_currency, to_currency, transaction_date))
frappe.msgprint(
_(
"Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually"
).format(from_currency, to_currency, transaction_date)
)
return 0.0
def enable_all_roles_and_domains():
""" enable all roles and domain for testing """
"""enable all roles and domain for testing"""
# add all roles to users
domains = frappe.get_all("Domain")
if not domains:
return
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
frappe.get_single('Domain Settings').set_active_domains(\
[d.name for d in domains])
add_all_roles_to('Administrator')
frappe.get_single("Domain Settings").set_active_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
@@ -145,12 +162,13 @@ def insert_record(records):
doc.insert(ignore_permissions=True)
except frappe.DuplicateEntryError as e:
# pass DuplicateEntryError and continue
if e.args and e.args[0]==doc.doctype and e.args[1]==doc.name:
if e.args and e.args[0] == doc.doctype and e.args[1] == doc.name:
# make sure DuplicateEntryError is for the exact same doc and not a related doc
pass
else:
raise
def welcome_email():
site_name = get_default_company() or "ERPNext"
title = _("Welcome to {0}").format(site_name)