mirror of
https://github.com/frappe/erpnext.git
synced 2026-09-14 17:33:09 +00:00
Merge remote-tracking branch 'upstream/develop' into remove-india
This commit is contained in:
@@ -1,56 +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()
|
||||
]
|
||||
|
||||
@@ -1,25 +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
|
||||
]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -104,61 +135,90 @@ class AuthorizationControl(TransactionBase):
|
||||
price_list_rate, base_rate = 0, 0
|
||||
for d in doc_obj.get("items"):
|
||||
if d.base_rate:
|
||||
price_list_rate += flt(d.base_price_list_rate) or flt(d.base_rate)
|
||||
base_rate += flt(d.base_rate)
|
||||
price_list_rate += (flt(d.base_price_list_rate) or flt(d.base_rate)) * flt(d.qty)
|
||||
base_rate += flt(d.base_rate) * flt(d.qty)
|
||||
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -5,5 +5,6 @@ import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Authorization Rule')
|
||||
|
||||
|
||||
class TestAuthorizationRule(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Brand')
|
||||
test_records = frappe.get_test_records("Brand")
|
||||
|
||||
@@ -233,7 +233,8 @@ erpnext.company.setup_queries = function(frm) {
|
||||
["expenses_included_in_asset_valuation", {"account_type": "Expenses Included In Asset Valuation"}],
|
||||
["capital_work_in_progress_account", {"account_type": "Capital Work in Progress"}],
|
||||
["asset_received_but_not_billed", {"account_type": "Asset Received But Not Billed"}],
|
||||
["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}]
|
||||
["unrealized_profit_loss_account", {"root_type": ["in", ["Liability", "Asset"]]}],
|
||||
["default_provisional_account", {"root_type": ["in", ["Liability", "Asset"]]}]
|
||||
], function(i, v) {
|
||||
erpnext.company.set_custom_query(frm, v);
|
||||
});
|
||||
|
||||
@@ -147,9 +147,8 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "domain",
|
||||
"fieldtype": "Link",
|
||||
"label": "Domain",
|
||||
"options": "Domain"
|
||||
"fieldtype": "Data",
|
||||
"label": "Domain"
|
||||
},
|
||||
{
|
||||
"fieldname": "parent_company",
|
||||
@@ -748,7 +747,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2022-01-25 10:33:16.826067",
|
||||
"modified": "2022-04-27 10:33:16.826067",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
||||
@@ -18,7 +18,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")
|
||||
@@ -26,12 +26,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
|
||||
|
||||
@@ -53,7 +65,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()
|
||||
|
||||
@@ -63,7 +75,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()
|
||||
@@ -72,37 +86,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()
|
||||
@@ -117,7 +151,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()
|
||||
@@ -127,12 +162,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()
|
||||
@@ -143,31 +181,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":
|
||||
@@ -184,34 +239,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"))
|
||||
@@ -225,29 +292,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)
|
||||
|
||||
@@ -255,32 +326,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)
|
||||
|
||||
@@ -288,35 +365,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:
|
||||
@@ -335,26 +416,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)
|
||||
@@ -369,26 +456,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)
|
||||
@@ -401,18 +499,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():
|
||||
@@ -420,6 +520,7 @@ def get_name_with_abbr(name, company):
|
||||
|
||||
return " - ".join(parts)
|
||||
|
||||
|
||||
def install_country_fixtures(company, country):
|
||||
try:
|
||||
module_name = f"erpnext.regional.{frappe.scrub(country)}.setup.setup"
|
||||
@@ -427,14 +528,19 @@ def install_country_fixtures(company, country):
|
||||
except ImportError:
|
||||
pass
|
||||
except Exception:
|
||||
frappe.log_error()
|
||||
frappe.throw(_("Failed to setup defaults for country {0}. Please contact support.").format(frappe.bold(country)))
|
||||
frappe.log_error("Unable to set country fixtures")
|
||||
frappe.throw(
|
||||
_("Failed to setup defaults for country {0}. Please contact support.").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
|
||||
@@ -446,44 +552,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
|
||||
@@ -492,25 +612,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 (
|
||||
@@ -550,46 +675,55 @@ 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]:
|
||||
@@ -600,11 +734,9 @@ def get_default_company_address(name, sort_key='is_primary_address', existing_ad
|
||||
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()
|
||||
|
||||
@@ -3,38 +3,25 @@ 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",
|
||||
"filters": {"docstatus": 1, "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"]},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -8,7 +8,8 @@
|
||||
"domain": "Manufacturing",
|
||||
"chart_of_accounts": "Standard",
|
||||
"default_holiday_list": "_Test Holiday List",
|
||||
"enable_perpetual_inventory": 0
|
||||
"enable_perpetual_inventory": 0,
|
||||
"allow_account_creation_against_child_company": 1
|
||||
},
|
||||
{
|
||||
"abbr": "_TC1",
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -9,20 +9,27 @@ 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):
|
||||
for record in 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"),
|
||||
)
|
||||
@@ -39,10 +46,8 @@ def save_new_records(test_records):
|
||||
curr_exchange.for_selling = record["for_selling"]
|
||||
curr_exchange.insert()
|
||||
|
||||
test_exchange_values = {
|
||||
'2015-12-15': '66.999',
|
||||
'2016-01-15': '65.1'
|
||||
}
|
||||
|
||||
test_exchange_values = {"2015-12-15": "66.999", "2016-01-15": "65.1"}
|
||||
|
||||
# Removing API call from get_exchange_rate
|
||||
def patched_requests_get(*args, **kwargs):
|
||||
@@ -58,19 +63,22 @@ def patched_requests_get(*args, **kwargs):
|
||||
def json(self):
|
||||
return self.json_data
|
||||
|
||||
if args[0] == "https://api.exchangerate.host/convert" and kwargs.get('params'):
|
||||
if kwargs['params'].get('date') and kwargs['params'].get('from') and kwargs['params'].get('to'):
|
||||
if test_exchange_values.get(kwargs['params']['date']):
|
||||
return PatchResponse({'result': test_exchange_values[kwargs['params']['date']]}, 200)
|
||||
elif args[0].startswith("https://frankfurter.app") and kwargs.get('params'):
|
||||
if kwargs['params'].get('base') and kwargs['params'].get('symbols'):
|
||||
if args[0] == "https://api.exchangerate.host/convert" and kwargs.get("params"):
|
||||
if kwargs["params"].get("date") and kwargs["params"].get("from") and kwargs["params"].get("to"):
|
||||
if test_exchange_values.get(kwargs["params"]["date"]):
|
||||
return PatchResponse({"result": test_exchange_values[kwargs["params"]["date"]]}, 200)
|
||||
elif args[0].startswith("https://frankfurter.app") and kwargs.get("params"):
|
||||
if kwargs["params"].get("base") and kwargs["params"].get("symbols"):
|
||||
date = args[0].replace("https://frankfurter.app/", "")
|
||||
if test_exchange_values.get(date):
|
||||
return PatchResponse({'rates': {kwargs['params'].get('symbols'): test_exchange_values.get(date)}}, 200)
|
||||
return PatchResponse(
|
||||
{"rates": {kwargs["params"].get("symbols"): test_exchange_values.get(date)}}, 200
|
||||
)
|
||||
|
||||
return PatchResponse({'rates': None}, 404)
|
||||
return PatchResponse({"rates": None}, 404)
|
||||
|
||||
@mock.patch('requests.get', side_effect=patched_requests_get)
|
||||
|
||||
@mock.patch("requests.get", side_effect=patched_requests_get)
|
||||
class TestCurrencyExchange(unittest.TestCase):
|
||||
def clear_cache(self):
|
||||
cache = frappe.cache()
|
||||
@@ -112,7 +120,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
|
||||
# Update Currency Exchange Rate
|
||||
settings = frappe.get_single("Currency Exchange Settings")
|
||||
settings.service_provider = 'exchangerate.host'
|
||||
settings.service_provider = "exchangerate.host"
|
||||
settings.save()
|
||||
|
||||
# Update exchange
|
||||
@@ -139,7 +147,7 @@ class TestCurrencyExchange(unittest.TestCase):
|
||||
self.assertEqual(flt(exchange_rate, 3), 65.1)
|
||||
|
||||
settings = frappe.get_single("Currency Exchange Settings")
|
||||
settings.service_provider = 'frankfurter.app'
|
||||
settings.service_provider = "frankfurter.app"
|
||||
settings.save()
|
||||
|
||||
def test_exchange_rate_strict(self, mock_get):
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -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
@@ -3,31 +3,63 @@ 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 world’s 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 world’s 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)
|
||||
|
||||
@@ -5,5 +5,6 @@ import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Email Digest')
|
||||
|
||||
|
||||
class TestEmailDigest(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -14,6 +14,16 @@ frappe.ui.form.on("Item Group", {
|
||||
]
|
||||
}
|
||||
}
|
||||
frm.fields_dict['item_group_defaults'].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) {
|
||||
const row = locals[cdt][cdn];
|
||||
return {
|
||||
filters: {
|
||||
'report_type': 'Profit and Loss',
|
||||
'company': row.company,
|
||||
"is_group": 0
|
||||
}
|
||||
};
|
||||
}
|
||||
frm.fields_dict["item_group_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) {
|
||||
const row = locals[cdt][cdn];
|
||||
return {
|
||||
@@ -62,17 +72,16 @@ frappe.ui.form.on("Item Group", {
|
||||
});
|
||||
}
|
||||
|
||||
frappe.model.with_doctype('Item', () => {
|
||||
const item_meta = frappe.get_meta('Item');
|
||||
frappe.model.with_doctype('Website Item', () => {
|
||||
const web_item_meta = frappe.get_meta('Website Item');
|
||||
|
||||
const valid_fields = item_meta.fields.filter(
|
||||
df => ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
|
||||
).map(df => ({ label: df.label, value: df.fieldname }));
|
||||
|
||||
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
||||
'fieldname', 'fieldtype', 'Select'
|
||||
const valid_fields = web_item_meta.fields.filter(df =>
|
||||
['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden
|
||||
).map(df =>
|
||||
({ label: df.label, value: df.fieldname })
|
||||
);
|
||||
frm.fields_dict.filter_fields.grid.update_docfield_property(
|
||||
|
||||
frm.get_field("filter_fields").grid.update_docfield_property(
|
||||
'fieldname', 'options', valid_fields
|
||||
);
|
||||
});
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
"sec_break_taxes",
|
||||
"taxes",
|
||||
"sb9",
|
||||
"show_in_website",
|
||||
"route",
|
||||
"weightage",
|
||||
"slideshow",
|
||||
"website_title",
|
||||
"description",
|
||||
"show_in_website",
|
||||
"include_descendants",
|
||||
"column_break_16",
|
||||
"weightage",
|
||||
"slideshow",
|
||||
"website_specifications",
|
||||
"website_filters_section",
|
||||
"filter_fields",
|
||||
@@ -111,7 +113,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Check this if you want to show in website",
|
||||
"description": "Make Item Group visible in website",
|
||||
"fieldname": "show_in_website",
|
||||
"fieldtype": "Check",
|
||||
"label": "Show in Website"
|
||||
@@ -124,6 +126,7 @@
|
||||
"unique": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "show_in_website",
|
||||
"fieldname": "weightage",
|
||||
"fieldtype": "Int",
|
||||
"label": "Weightage"
|
||||
@@ -186,6 +189,8 @@
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "show_in_website",
|
||||
"fieldname": "website_filters_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Website Filters"
|
||||
@@ -203,9 +208,22 @@
|
||||
"options": "Website Attribute"
|
||||
},
|
||||
{
|
||||
"depends_on": "show_in_website",
|
||||
"fieldname": "website_title",
|
||||
"fieldtype": "Data",
|
||||
"label": "Title"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_16",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "show_in_website",
|
||||
"description": "Include Website Items belonging to child Item Groups",
|
||||
"fieldname": "include_descendants",
|
||||
"fieldtype": "Check",
|
||||
"label": "Include Descendants"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-sitemap",
|
||||
@@ -214,11 +232,12 @@
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"max_attachments": 3,
|
||||
"modified": "2021-02-18 13:40:30.049650",
|
||||
"modified": "2022-03-09 12:27:11.055782",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Item Group",
|
||||
"name_case": "Title Case",
|
||||
"naming_rule": "By fieldname",
|
||||
"nsm_parent_field": "parent_item_group",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
@@ -285,5 +304,6 @@
|
||||
"search_fields": "parent_item_group",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
@@ -11,16 +11,17 @@ from frappe.utils.nestedset import NestedSet
|
||||
from frappe.website.utils import clear_cache
|
||||
from frappe.website.website_generator import WebsiteGenerator
|
||||
|
||||
from erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings import ECommerceSettings
|
||||
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):
|
||||
@@ -30,11 +31,12 @@ 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()
|
||||
ECommerceSettings.validate_field_filters(self.filter_fields, enable_field_filters=True)
|
||||
|
||||
def on_update(self):
|
||||
NestedSet.on_update(self)
|
||||
@@ -43,15 +45,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)
|
||||
|
||||
@@ -65,28 +67,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
|
||||
@@ -109,61 +105,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):
|
||||
|
||||
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
|
||||
|
||||
return frappe.get_all(
|
||||
"Item Group",
|
||||
filters=filters,
|
||||
fields=["name", "route"]
|
||||
)
|
||||
if include_self:
|
||||
filters.update({"lft": [">=", item_group.lft], "rgt": ["<=", item_group.rgt]})
|
||||
|
||||
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,
|
||||
]
|
||||
|
||||
@@ -171,21 +170,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)
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
Tool to set numbering (naming) series for various DocTypes.
|
||||
@@ -1,55 +0,0 @@
|
||||
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
|
||||
frappe.ui.form.on("Naming Series", {
|
||||
onload: function(frm) {
|
||||
frm.disable_save();
|
||||
frm.events.get_doc_and_prefix(frm);
|
||||
},
|
||||
|
||||
get_doc_and_prefix: function(frm) {
|
||||
frappe.call({
|
||||
method: "get_transactions",
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
frm.set_df_property("select_doc_for_series", "options", r.message.transactions);
|
||||
frm.set_df_property("prefix", "options", r.message.prefixes);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
select_doc_for_series: function(frm) {
|
||||
frm.set_value("user_must_always_select", 0);
|
||||
frappe.call({
|
||||
method: "get_options",
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
frm.set_value("set_options", r.message);
|
||||
if(r.message && r.message.split('\n')[0]=='')
|
||||
frm.set_value('user_must_always_select', 1);
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
prefix: function(frm) {
|
||||
frappe.call({
|
||||
method: "get_current",
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
frm.refresh_field("current_value");
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
update: function(frm) {
|
||||
frappe.call({
|
||||
method: "update_series",
|
||||
doc: frm.doc,
|
||||
callback: function(r) {
|
||||
frm.events.get_doc_and_prefix(frm);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@@ -1,360 +0,0 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"creation": "2013-01-25 11:35:08",
|
||||
"custom": 0,
|
||||
"description": "Set prefix for numbering series on your transactions",
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 0,
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Set prefix for numbering series on your transactions",
|
||||
"fieldname": "setup_series",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Setup Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "select_doc_for_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Select Transaction",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "select_doc_for_series",
|
||||
"fieldname": "help_html",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Help HTML",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "<div class=\"well\">\nEdit list of Series in the box below. Rules:\n<ul>\n<li>Each Series Prefix on a new line.</li>\n<li>Allowed special characters are \"/\" and \"-\"</li>\n<li>Optionally, set the number of digits in the series using dot (.) followed by hashes (#). For example, \".####\" means that the series will have four digits. Default is five digits.</li>\n</ul>\nExamples:<br>\nINV-<br>\nINV-10-<br>\nINVK-<br>\nINV-.####<br>\n</div>",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "select_doc_for_series",
|
||||
"fieldname": "set_options",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Series List for this Transaction",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "select_doc_for_series",
|
||||
"description": "Check this if you want to force the user to select a series before saving. There will be no default if you check this.",
|
||||
"fieldname": "user_must_always_select",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "User must always select",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "select_doc_for_series",
|
||||
"fieldname": "update",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Update",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "Change the starting / current sequence number of an existing series.",
|
||||
"fieldname": "update_series",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Update Series",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "prefix",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Prefix",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "This is the number of the last created transaction with this prefix",
|
||||
"fieldname": "current_value",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Current Value",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "update_series_start",
|
||||
"fieldtype": "Button",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Update Series Number",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "update_series_start",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_heading": 0,
|
||||
"hide_toolbar": 1,
|
||||
"icon": "fa fa-sort-by-order",
|
||||
"idx": 1,
|
||||
"image_view": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-17 03:41:37.685910",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Naming Series",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 0,
|
||||
"read_only": 1,
|
||||
"read_only_onload": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"track_changes": 0,
|
||||
"track_seen": 0
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _, msgprint, throw
|
||||
from frappe.core.doctype.doctype.doctype import validate_series
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.naming import parse_naming_series
|
||||
from frappe.permissions import get_doctypes_with_read
|
||||
from frappe.utils import cint, cstr
|
||||
|
||||
|
||||
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(get_doctypes_with_read()).intersection(set(doctypes)))
|
||||
prefixes = ""
|
||||
for d in doctypes:
|
||||
options = ""
|
||||
try:
|
||||
options = self.get_options(d)
|
||||
except frappe.DoesNotExistError:
|
||||
frappe.msgprint(_('Unable to find DocType {0}').format(d))
|
||||
#frappe.pass_does_not_exist_error()
|
||||
continue
|
||||
|
||||
if options:
|
||||
prefixes = prefixes + "\n" + options
|
||||
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'])})
|
||||
if 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
|
||||
}
|
||||
|
||||
def scrub_options_list(self, ol):
|
||||
options = list(filter(lambda x: x, [cstr(n).strip() for n in ol]))
|
||||
return options
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_series(self, arg=None):
|
||||
"""update series list"""
|
||||
self.validate_series_set()
|
||||
self.check_duplicate()
|
||||
series_list = self.set_options.split("\n")
|
||||
|
||||
# set in doctype
|
||||
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()])
|
||||
|
||||
msgprint(_("Series Updated"))
|
||||
|
||||
return self.get_transactions()
|
||||
|
||||
def validate_series_set(self):
|
||||
if self.select_doc_for_series and not self.set_options:
|
||||
frappe.throw(_("Please set the series to be used."))
|
||||
|
||||
def set_series_for(self, doctype, ol):
|
||||
options = self.scrub_options_list(ol)
|
||||
|
||||
# validate names
|
||||
for i in options:
|
||||
self.validate_series_name(i)
|
||||
|
||||
if options and self.user_must_always_select:
|
||||
options = [''] + options
|
||||
|
||||
default = options[0] if options else ''
|
||||
|
||||
# update in property setter
|
||||
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})
|
||||
|
||||
if 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.save()
|
||||
|
||||
self.set_options = "\n".join(options)
|
||||
|
||||
frappe.clear_cache(doctype=doctype)
|
||||
|
||||
def check_duplicate(self):
|
||||
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
|
||||
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)
|
||||
))
|
||||
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"))
|
||||
for series in options:
|
||||
validate_series(dt, series)
|
||||
for i in sr:
|
||||
if i[0]:
|
||||
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]))
|
||||
|
||||
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'))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_options(self, arg=None):
|
||||
if frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series"):
|
||||
return frappe.get_meta(arg or self.select_doc_for_series).get_field("naming_series").options
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_current(self, arg=None):
|
||||
"""get series current"""
|
||||
if self.prefix:
|
||||
prefix = self.parse_naming_series()
|
||||
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:
|
||||
frappe.db.sql("insert into tabSeries (name, current) values (%s, 0)", (series))
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_series_start(self):
|
||||
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))
|
||||
msgprint(_("Series Updated Successfully"))
|
||||
else:
|
||||
msgprint(_("Please select prefix first"))
|
||||
|
||||
def parse_naming_series(self):
|
||||
parts = self.prefix.split('.')
|
||||
|
||||
# Remove ### from the end of series
|
||||
if parts[-1] == "#" * len(parts[-1]):
|
||||
del parts[-1]
|
||||
|
||||
prefix = parse_naming_series(parts)
|
||||
return prefix
|
||||
|
||||
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)
|
||||
|
||||
# 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))
|
||||
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)
|
||||
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)
|
||||
|
||||
if hide_name_field:
|
||||
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))
|
||||
|
||||
def get_default_naming_series(doctype):
|
||||
naming_series = frappe.get_meta(doctype).get_field("naming_series").options or ""
|
||||
naming_series = naming_series.split("\n")
|
||||
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)
|
||||
else:
|
||||
return out
|
||||
@@ -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 %(page_len)s offset %(start)s""".format(
|
||||
key=searchfield, cond=cond
|
||||
),
|
||||
{"txt": "%" + txt + "%", "start": start, "page_len": page_len},
|
||||
)
|
||||
|
||||
@@ -5,5 +5,6 @@ import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Party Type')
|
||||
|
||||
|
||||
class TestPartyType(unittest.TestCase):
|
||||
pass
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Print Heading')
|
||||
test_records = frappe.get_test_records("Print Heading")
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Quotation Lost Reason')
|
||||
test_records = frappe.get_test_records("Quotation Lost Reason")
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -4,8 +4,12 @@
|
||||
frappe.ui.form.on('Sales Person', {
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.__onload && frm.doc.__onload.dashboard_info) {
|
||||
var info = frm.doc.__onload.dashboard_info;
|
||||
frm.dashboard.add_indicator(__('Total Contribution Amount: {0}', [format_currency(info.allocated_amount, info.currency)]), 'blue');
|
||||
let info = frm.doc.__onload.dashboard_info;
|
||||
frm.dashboard.add_indicator(__('Total Contribution Amount Against Orders: {0}',
|
||||
[format_currency(info.allocated_amount_against_order, info.currency)]), 'blue');
|
||||
|
||||
frm.dashboard.add_indicator(__('Total Contribution Amount Against Invoices: {0}',
|
||||
[format_currency(info.allocated_amount_against_invoice, info.currency)]), 'blue');
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -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,28 @@ class SalesPerson(NestedSet):
|
||||
def load_dashboard_info(self):
|
||||
company_default_currency = get_default_currency()
|
||||
|
||||
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))
|
||||
allocated_amount_against_order = flt(
|
||||
frappe.db.get_value(
|
||||
"Sales Team",
|
||||
{"docstatus": 1, "parenttype": "Sales Order", "sales_person": self.sales_person_name},
|
||||
"sum(allocated_amount)",
|
||||
)
|
||||
)
|
||||
|
||||
allocated_amount_against_invoice = flt(
|
||||
frappe.db.get_value(
|
||||
"Sales Team",
|
||||
{"docstatus": 1, "parenttype": "Sales Invoice", "sales_person": self.sales_person_name},
|
||||
"sum(allocated_amount)",
|
||||
)
|
||||
)
|
||||
|
||||
info = {}
|
||||
info["allocated_amount"] = flt(allocated_amount[0][0]) if allocated_amount else 0
|
||||
info["allocated_amount_against_order"] = allocated_amount_against_order
|
||||
info["allocated_amount_against_invoice"] = allocated_amount_against_invoice
|
||||
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 +68,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 +115,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):
|
||||
|
||||
@@ -3,13 +3,12 @@ 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"]},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Supplier Group')
|
||||
test_records = frappe.get_test_records("Supplier Group")
|
||||
|
||||
@@ -15,9 +15,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, str):
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Terms and Conditions')
|
||||
test_records = frappe.get_test_records("Terms and Conditions")
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('Territory')
|
||||
test_records = frappe.get_test_records("Territory")
|
||||
|
||||
test_ignore = ["Item Group"]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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,103 @@ 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))
|
||||
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']
|
||||
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",
|
||||
]
|
||||
return doctypes_to_be_ignored_list
|
||||
|
||||
@@ -3,4 +3,4 @@
|
||||
|
||||
import frappe
|
||||
|
||||
test_records = frappe.get_test_records('UOM')
|
||||
test_records = frappe.get_test_records("UOM")
|
||||
|
||||
@@ -19,11 +19,9 @@ 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()
|
||||
create_print_zero_amount_taxes_custom_field()
|
||||
create_print_setting_custom_fields()
|
||||
add_all_roles_to("Administrator")
|
||||
create_default_cash_flow_mapper_templates()
|
||||
create_default_success_action()
|
||||
@@ -36,25 +34,33 @@ 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)
|
||||
doc = frappe.get_doc(dt, dt)
|
||||
for fieldname, value in default_values:
|
||||
b.set(fieldname, value)
|
||||
b.save()
|
||||
except frappe.MandatoryError:
|
||||
pass
|
||||
doc.set(fieldname, value)
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save()
|
||||
except frappe.ValidationError:
|
||||
pass
|
||||
|
||||
@@ -62,127 +68,140 @@ def set_single_defaults():
|
||||
|
||||
setup_currency_exchange()
|
||||
|
||||
|
||||
def setup_currency_exchange():
|
||||
ces = frappe.get_single('Currency Exchange Settings')
|
||||
ces = frappe.get_single("Currency Exchange Settings")
|
||||
try:
|
||||
ces.set('result_key', [])
|
||||
ces.set('req_params', [])
|
||||
ces.set("result_key", [])
|
||||
ces.set("req_params", [])
|
||||
|
||||
ces.api_endpoint = "https://frankfurter.app/{transaction_date}"
|
||||
ces.append('result_key', {'key': 'rates'})
|
||||
ces.append('result_key', {'key': '{to_currency}'})
|
||||
ces.append('req_params', {'key': 'base', 'value': '{from_currency}'})
|
||||
ces.append('req_params', {'key': 'symbols', 'value': '{to_currency}'})
|
||||
ces.append("result_key", {"key": "rates"})
|
||||
ces.append("result_key", {"key": "{to_currency}"})
|
||||
ces.append("req_params", {"key": "base", "value": "{from_currency}"})
|
||||
ces.append("req_params", {"key": "symbols", "value": "{to_currency}"})
|
||||
ces.save()
|
||||
except frappe.ValidationError:
|
||||
pass
|
||||
|
||||
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'
|
||||
})
|
||||
|
||||
|
||||
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'
|
||||
})
|
||||
|
||||
|
||||
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'
|
||||
})
|
||||
def create_print_setting_custom_fields():
|
||||
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": _("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 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()
|
||||
@@ -191,91 +210,95 @@ def add_non_standard_user_types():
|
||||
for user_type, data in user_types.items():
|
||||
user_type_limit.setdefault(frappe.scrub(user_type), 20)
|
||||
|
||||
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 user_types.items():
|
||||
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': {
|
||||
"Employee Self Service": {
|
||||
"role": "Employee Self Service",
|
||||
"apply_user_permission_on": "Employee",
|
||||
"user_id_field": "user_id",
|
||||
"doctypes": {
|
||||
# masters
|
||||
'Holiday List': ['read'],
|
||||
'Employee': ['read', 'write'],
|
||||
"Holiday List": ["read"],
|
||||
"Employee": ["read", "write"],
|
||||
# payroll
|
||||
'Salary Slip': ['read'],
|
||||
'Employee Benefit Application': ['read', 'write', 'create', 'delete'],
|
||||
"Salary Slip": ["read"],
|
||||
"Employee Benefit Application": ["read", "write", "create", "delete"],
|
||||
# expenses
|
||||
'Expense Claim': ['read', 'write', 'create', 'delete'],
|
||||
'Employee Advance': ['read', 'write', 'create', 'delete'],
|
||||
"Expense Claim": ["read", "write", "create", "delete"],
|
||||
"Employee Advance": ["read", "write", "create", "delete"],
|
||||
# leave and attendance
|
||||
'Leave Application': ['read', 'write', 'create', 'delete'],
|
||||
'Attendance Request': ['read', 'write', 'create', 'delete'],
|
||||
'Compensatory Leave Request': ['read', 'write', 'create', 'delete'],
|
||||
"Leave Application": ["read", "write", "create", "delete"],
|
||||
"Attendance Request": ["read", "write", "create", "delete"],
|
||||
"Compensatory Leave Request": ["read", "write", "create", "delete"],
|
||||
# tax
|
||||
'Employee Tax Exemption Declaration': ['read', 'write', 'create', 'delete'],
|
||||
'Employee Tax Exemption Proof Submission': ['read', 'write', 'create', 'delete'],
|
||||
"Employee Tax Exemption Declaration": ["read", "write", "create", "delete"],
|
||||
"Employee Tax Exemption Proof Submission": ["read", "write", "create", "delete"],
|
||||
# projects
|
||||
'Timesheet': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend'],
|
||||
"Timesheet": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
|
||||
# trainings
|
||||
'Training Program': ['read'],
|
||||
'Training Feedback': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend'],
|
||||
"Training Program": ["read"],
|
||||
"Training Feedback": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
|
||||
# shifts
|
||||
'Shift Request': ['read', 'write', 'create', 'delete', 'submit', 'cancel', 'amend'],
|
||||
"Shift Request": ["read", "write", "create", "delete", "submit", "cancel", "amend"],
|
||||
# misc
|
||||
'Employee Grievance': ['read', 'write', 'create', 'delete'],
|
||||
'Employee Referral': ['read', 'write', 'create', 'delete'],
|
||||
'Travel Request': ['read', 'write', 'create', 'delete']
|
||||
}
|
||||
"Employee Grievance": ["read", "write", "create", "delete"],
|
||||
"Employee Referral": ["read", "write", "create", "delete"],
|
||||
"Travel Request": ["read", "write", "create", "delete"],
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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 data.get('doctypes').items():
|
||||
args = {'document_type': doctype}
|
||||
for doctype, perms in data.get("doctypes").items():
|
||||
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
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
"documentation_url": "https://docs.erpnext.com/docs/v13/user/manual/en/setting-up/company-setup",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"modified": "2021-12-15 14:23:52.460913",
|
||||
"modified": "2022-06-07 14:31:00.575193",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Home",
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
"action": "Watch Video",
|
||||
"action_label": "Learn more about data migration",
|
||||
"creation": "2021-05-19 05:29:16.809610",
|
||||
"description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc). If you are migrating from [Tally](https://tallysolutions.com/) or [Quickbooks](https://quickbooks.intuit.com/in/), we got special migration tools for you.",
|
||||
"description": "# Import Data from Spreadsheet\n\nIn ERPNext, you can easily migrate your historical data using spreadsheets. You can use it for migrating not just masters (like Customer, Supplier, Items), but also for transactions like (outstanding invoices, opening stock and accounting entries, etc).",
|
||||
"docstatus": 0,
|
||||
"doctype": "Onboarding Step",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2021-12-15 13:10:57.346422",
|
||||
"modified": "2022-06-07 14:28:51.390813",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Data import",
|
||||
"owner": "Administrator",
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
"is_complete": 0,
|
||||
"is_single": 0,
|
||||
"is_skipped": 0,
|
||||
"modified": "2021-12-15 14:20:55.441678",
|
||||
"modified": "2022-06-07 14:28:00.901082",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Navigation Help",
|
||||
"owner": "Administrator",
|
||||
|
||||
@@ -13,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)
|
||||
@@ -25,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": [
|
||||
@@ -54,7 +53,7 @@ def get_default_dashboards():
|
||||
"timeseries": 1,
|
||||
"owner": "Administrator",
|
||||
"type": "Line",
|
||||
"width": "Half"
|
||||
"width": "Half",
|
||||
},
|
||||
{
|
||||
"doctype": "Dashboard Chart",
|
||||
@@ -68,7 +67,7 @@ def get_default_dashboards():
|
||||
"timeseries": 1,
|
||||
"owner": "Administrator",
|
||||
"type": "Line",
|
||||
"width": "Half"
|
||||
"width": "Half",
|
||||
},
|
||||
{
|
||||
"doctype": "Dashboard Chart",
|
||||
@@ -82,7 +81,7 @@ def get_default_dashboards():
|
||||
"timeseries": 1,
|
||||
"owner": "Administrator",
|
||||
"type": "Line",
|
||||
"width": "Half"
|
||||
"width": "Half",
|
||||
},
|
||||
{
|
||||
"doctype": "Dashboard Chart",
|
||||
@@ -98,7 +97,7 @@ def get_default_dashboards():
|
||||
"owner": "Administrator",
|
||||
"document_type": "Purchase Invoice",
|
||||
"type": "Bar",
|
||||
"width": "Half"
|
||||
"width": "Half",
|
||||
},
|
||||
{
|
||||
"doctype": "Dashboard Chart",
|
||||
@@ -114,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",
|
||||
@@ -140,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:
|
||||
|
||||
@@ -3,55 +3,55 @@ 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"),
|
||||
]
|
||||
|
||||
@@ -6,42 +6,51 @@ from frappe import _
|
||||
from frappe.utils import cstr, getdate
|
||||
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"),
|
||||
}
|
||||
).insert()
|
||||
|
||||
def enable_shopping_cart(args): # nosemgrep
|
||||
|
||||
def enable_shopping_cart(args): # nosemgrep
|
||||
# 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:
|
||||
@@ -49,20 +58,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":
|
||||
@@ -73,39 +82,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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -5,17 +5,20 @@ import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, getdate
|
||||
|
||||
|
||||
def set_default_settings(args):
|
||||
# enable default currency
|
||||
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()
|
||||
|
||||
@@ -23,13 +26,12 @@ 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'))
|
||||
|
||||
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 +74,74 @@ def set_default_settings(args):
|
||||
hr_settings.exit_questionnaire_notification_template = _("Exit Questionnaire Notification")
|
||||
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
@@ -1,179 +0,0 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
|
||||
import frappe
|
||||
import frappe.utils
|
||||
from frappe import _
|
||||
from frappe.utils.make_random import add_random_children
|
||||
|
||||
|
||||
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})
|
||||
customers = frappe.get_all("Customer")
|
||||
warehouses = frappe.get_all("Warehouse")
|
||||
|
||||
if items and customers:
|
||||
for i in range(3):
|
||||
customer = random.choice(customers).name
|
||||
make_opportunity(items, customer)
|
||||
make_quote(items, customer)
|
||||
|
||||
if items and warehouses:
|
||||
make_material_request(frappe.get_all("Item"))
|
||||
|
||||
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")
|
||||
|
||||
b.insert(ignore_permissions=True)
|
||||
|
||||
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"
|
||||
})
|
||||
|
||||
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")
|
||||
|
||||
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": [{
|
||||
"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")
|
||||
|
||||
|
||||
def make_issue():
|
||||
pass
|
||||
|
||||
def make_projects(domains):
|
||||
current_date = frappe.utils.nowdate()
|
||||
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"
|
||||
}]
|
||||
|
||||
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"
|
||||
},
|
||||
{
|
||||
"title": "Setup Master Data",
|
||||
"start_date": current_date,
|
||||
"end_date": frappe.utils.add_days(current_date, 1),
|
||||
"file": "education_masters.md"
|
||||
}]
|
||||
|
||||
else:
|
||||
tasks += [
|
||||
{
|
||||
"title": "Setup Your Company",
|
||||
"start_date": current_date,
|
||||
"end_date": frappe.utils.add_days(current_date, 1),
|
||||
"file": "masters.md"
|
||||
},
|
||||
{
|
||||
"title": "Start Tracking your Sales",
|
||||
"start_date": current_date,
|
||||
"end_date": frappe.utils.add_days(current_date, 2),
|
||||
"file": "sales.md"
|
||||
},
|
||||
{
|
||||
"title": "Start Managing Purchases",
|
||||
"start_date": current_date,
|
||||
"end_date": frappe.utils.add_days(current_date, 3),
|
||||
"file": "purchase.md"
|
||||
},
|
||||
{
|
||||
"title": "Import Data",
|
||||
"start_date": current_date,
|
||||
"end_date": frappe.utils.add_days(current_date, 4),
|
||||
"file": "import_data.md"
|
||||
},
|
||||
{
|
||||
"title": "Go Live!",
|
||||
"start_date": current_date,
|
||||
"end_date": frappe.utils.add_days(current_date, 5),
|
||||
"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']
|
||||
|
||||
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:
|
||||
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')
|
||||
import_notification()
|
||||
@@ -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()
|
||||
frappe.log_error("Unable to setup regional tax settings")
|
||||
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,36 +194,36 @@ def make_item_tax_template(company_name, template):
|
||||
doc.insert(ignore_permissions=True)
|
||||
return doc
|
||||
|
||||
|
||||
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
|
||||
@@ -220,50 +231,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
|
||||
@@ -275,11 +289,11 @@ def get_or_create_tax_group(company_name, root_type):
|
||||
|
||||
|
||||
def make_tax_category(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)
|
||||
|
||||
@@ -7,112 +7,69 @@ from frappe import _
|
||||
|
||||
from .operations import company_setup
|
||||
from .operations import install_fixtures as fixtures
|
||||
from .operations import sample_data
|
||||
|
||||
|
||||
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")},
|
||||
],
|
||||
},
|
||||
{
|
||||
'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'))
|
||||
|
||||
def make_sample_data(domains):
|
||||
try:
|
||||
sample_data.make_sample_data(domains)
|
||||
except Exception:
|
||||
# clear message
|
||||
if frappe.message_log:
|
||||
frappe.message_log.pop()
|
||||
pass
|
||||
|
||||
def login_as_first_user(args):
|
||||
if args.get("email") and hasattr(frappe.local, "login_manager"):
|
||||
@@ -126,7 +83,3 @@ def setup_complete(args=None):
|
||||
setup_defaults(args)
|
||||
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'))
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
Lets start making things in ERPNext that are representative of your institution.
|
||||
|
||||
1. Make a list of **Programs** that you offer
|
||||
1. Add a few **Courses** that your programs cover
|
||||
1. Create **Academic Terms** and **Academic Years**
|
||||
1. Start adding **Students**
|
||||
1. Group your students into **Batches**
|
||||
|
||||
Watch this video to learn more about ERPNext Education: https://www.youtube.com/watch?v=f6foQOyGzdA
|
||||
@@ -1,7 +0,0 @@
|
||||
Thanks for checking this out! ❤️
|
||||
|
||||
If you are evaluating an ERP system for the first time, this is going to be quite a task! But don't worry, ERPNext is awesome.
|
||||
|
||||
First, get familiar with the surroundings. ERPNext covers a *lot of features*, go to the home page and click on the "Explore" icon.
|
||||
|
||||
All the best!
|
||||
@@ -1,18 +0,0 @@
|
||||
Ready to go live with ERPNext? 🏁🏁🏁
|
||||
|
||||
Here are the steps:
|
||||
|
||||
1. Sync up your **Chart of Accounts**
|
||||
3. Add your opening stock using **Stock Reconciliation**
|
||||
4. Add your open invoices (both sales and purchase)
|
||||
3. Add your opening account balances by making a **Journal Entry**
|
||||
|
||||
If you need help for going live, sign up for an account at erpnext.com or find a partner to help you with this.
|
||||
|
||||
Or you can watch these videos 📺:
|
||||
|
||||
Setup your chart of accounts: https://www.youtube.com/watch?v=AcfMCT7wLLo
|
||||
|
||||
Add Open Stock: https://www.youtube.com/watch?v=nlHX0ZZ84Lw
|
||||
|
||||
Add Opening Balances: https://www.youtube.com/watch?v=nlHX0ZZ84Lw
|
||||
@@ -1,5 +0,0 @@
|
||||
Lets import some data! 💪💪
|
||||
|
||||
If you are already running a business, you most likely have your Items, Customers or Suppliers in some spreadsheet file somewhere, import it into ERPNext with the Data Import Tool.
|
||||
|
||||
Watch this video to get started: https://www.youtube.com/watch?v=Ta2Xx3QoK3E
|
||||
@@ -1,7 +0,0 @@
|
||||
Start building a model of your business in ERPNext by adding your Items and Customers.
|
||||
|
||||
These videos 📺 will help you get started:
|
||||
|
||||
Adding Customers and Suppliers: https://www.youtube.com/watch?v=zsrrVDk6VBs
|
||||
|
||||
Adding Items and Prices: https://www.youtube.com/watch?v=FcOsV-e8ymE
|
||||
@@ -1,10 +0,0 @@
|
||||
How to manage your purchasing in ERPNext 🛒🛒🛒:
|
||||
|
||||
1. Add a few **Suppliers**
|
||||
2. Find out what you need by making **Material Requests**.
|
||||
3. Now start placing orders via **Purchase Order**.
|
||||
4. When your suppliers deliver, make **Purchase Receipts**
|
||||
|
||||
Now never run out of stock again! 😎
|
||||
|
||||
Watch this video 📺 to get an overview: https://www.youtube.com/watch?v=4TN9kPyfIqM
|
||||
@@ -1,8 +0,0 @@
|
||||
Start managing your sales with ERPNext 🔔🔔🔔:
|
||||
|
||||
1. Add potential business contacts as **Leads**
|
||||
2. Udpate your deals in pipeline in **Opportunities**
|
||||
3. Send proposals to your leads or customers with **Quotations**
|
||||
4. Track confirmed orders with **Sales Orders**
|
||||
|
||||
Watch this video 📺 to get an overview: https://www.youtube.com/watch?v=o9XCSZHJfpA
|
||||
@@ -1,5 +0,0 @@
|
||||
Lets import some data! 💪💪
|
||||
|
||||
If you are already running a Institute, you most likely have your Students in some spreadsheet file somewhere. Import it into ERPNext with the Data Import Tool.
|
||||
|
||||
Watch this video to get started: https://www.youtube.com/watch?v=Ta2Xx3QoK3E
|
||||
@@ -1,28 +0,0 @@
|
||||
[
|
||||
{
|
||||
"attach_print": 0,
|
||||
"condition": "doc.status in ('Open', 'Overdue')",
|
||||
"date_changed": "exp_end_date",
|
||||
"days_in_advance": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Notification",
|
||||
"document_type": "Task",
|
||||
"enabled": 1,
|
||||
"event": "Days After",
|
||||
"is_standard": 0,
|
||||
"message": "<p>Task due today:</p>\n\n<div>\n{{ doc.description }}\n</div>\n\n<hr>\n<p style=\"font-size: 85%\">\nThis is a notification for a task that is due today, and a sample <b>Notification</b>. In ERPNext you can setup notifications on anything, Invoices, Orders, Leads, Opportunities, so you never miss a thing.\n<br>To edit this, and setup other alerts, just type <b>Notification</b> in the search bar.</p>",
|
||||
"method": null,
|
||||
"modified": "2017-03-09 07:34:58.168370",
|
||||
"module": null,
|
||||
"name": "Task Due Alert",
|
||||
"recipients": [
|
||||
{
|
||||
"cc": null,
|
||||
"condition": null,
|
||||
"email_by_document_field": "owner"
|
||||
}
|
||||
],
|
||||
"subject": "{{ doc.subject }}",
|
||||
"value_changed": null
|
||||
}
|
||||
]
|
||||
@@ -1,62 +1,53 @@
|
||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import add_days, flt, get_datetime_str, nowdate
|
||||
from frappe.utils.data import now_datetime
|
||||
from frappe.utils.nestedset import get_ancestors_of, get_root_of # noqa
|
||||
|
||||
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))
|
||||
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))
|
||||
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" :"2021-01-01",
|
||||
"fy_end_date" :"2021-12-31",
|
||||
"language" :"english",
|
||||
"company_tagline" :"Testing",
|
||||
"email" :"test@erpnext.com",
|
||||
"password" :"test",
|
||||
"chart_of_accounts" : "Standard",
|
||||
"domains" : ["Manufacturing"],
|
||||
})
|
||||
|
||||
if not frappe.db.a_row_exists("Company"):
|
||||
current_year = now_datetime().year
|
||||
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": f"{current_year}-01-01",
|
||||
"fy_end_date": f"{current_year}-12-31",
|
||||
"language": "english",
|
||||
"company_tagline": "Testing",
|
||||
"email": "test@erpnext.com",
|
||||
"password": "test",
|
||||
"chart_of_accounts": "Standard",
|
||||
}
|
||||
)
|
||||
|
||||
frappe.db.sql("delete from `tabLeave Allocation`")
|
||||
frappe.db.sql("delete from `tabLeave Application`")
|
||||
frappe.db.sql("delete from `tabSalary Slip`")
|
||||
frappe.db.sql("delete from `tabItem Price`")
|
||||
|
||||
frappe.db.set_value("Stock Settings", None, "auto_insert_price_list_rate_if_missing", 0)
|
||||
enable_all_roles_and_domains()
|
||||
_enable_all_roles_for_admin()
|
||||
|
||||
set_defaults_for_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 +64,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 +79,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,11 +91,12 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
|
||||
|
||||
if not value:
|
||||
import requests
|
||||
settings = frappe.get_cached_doc('Currency Exchange Settings')
|
||||
|
||||
settings = frappe.get_cached_doc("Currency Exchange Settings")
|
||||
req_params = {
|
||||
"transaction_date": transaction_date,
|
||||
"from_currency": from_currency,
|
||||
"to_currency": to_currency
|
||||
"to_currency": to_currency,
|
||||
}
|
||||
params = {}
|
||||
for row in settings.req_params:
|
||||
@@ -118,51 +110,56 @@ def get_exchange_rate(from_currency, to_currency, transaction_date=None, args=No
|
||||
cache.setex(name=key, time=21600, value=flt(value))
|
||||
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.log_error("Unable to fetch 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)
|
||||
)
|
||||
return 0.0
|
||||
|
||||
|
||||
def format_ces_api(data, param):
|
||||
return data.format(
|
||||
transaction_date=param.get("transaction_date"),
|
||||
to_currency=param.get("to_currency"),
|
||||
from_currency=param.get("from_currency")
|
||||
from_currency=param.get("from_currency"),
|
||||
)
|
||||
|
||||
def enable_all_roles_and_domains():
|
||||
""" enable all roles and domain for testing """
|
||||
# add all roles to users
|
||||
domains = frappe.get_all("Domain")
|
||||
if not domains:
|
||||
return
|
||||
|
||||
def enable_all_roles_and_domains():
|
||||
"""enable all roles and domain for testing"""
|
||||
_enable_all_roles_for_admin()
|
||||
|
||||
|
||||
def _enable_all_roles_for_admin():
|
||||
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')
|
||||
|
||||
all_roles = set(frappe.db.get_values("Role", pluck="name"))
|
||||
admin_roles = set(
|
||||
frappe.db.get_values("Has Role", {"parent": "Administrator"}, fieldname="role", pluck="role")
|
||||
)
|
||||
|
||||
if all_roles.difference(admin_roles):
|
||||
add_all_roles_to("Administrator")
|
||||
|
||||
|
||||
def set_defaults_for_tests():
|
||||
from frappe.utils.nestedset import get_root_of
|
||||
|
||||
selling_settings = frappe.get_single("Selling Settings")
|
||||
selling_settings.customer_group = get_root_of("Customer Group")
|
||||
selling_settings.territory = get_root_of("Territory")
|
||||
selling_settings.save()
|
||||
defaults = {
|
||||
"customer_group": get_root_of("Customer Group"),
|
||||
"territory": get_root_of("Territory"),
|
||||
}
|
||||
frappe.db.set_single_value("Selling Settings", defaults)
|
||||
for key, value in defaults.items():
|
||||
frappe.db.set_default(key, value)
|
||||
frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 0)
|
||||
|
||||
|
||||
def insert_record(records):
|
||||
for r in records:
|
||||
doc = frappe.new_doc(r.get("doctype"))
|
||||
doc.update(r)
|
||||
try:
|
||||
doc.insert(ignore_permissions=True, ignore_if_duplicate=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:
|
||||
# make sure DuplicateEntryError is for the exact same doc and not a related doc
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
from frappe.desk.page.setup_wizard.setup_wizard import make_records
|
||||
|
||||
make_records(records)
|
||||
|
||||
|
||||
def welcome_email():
|
||||
site_name = get_default_company() or "ERPNext"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"charts": [],
|
||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t\\n\\t\\t\\t\\n\\t\\t</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Projects Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"HR Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Selling Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Buying Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Support Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Shopping Cart Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Portal Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Domain Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Products Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Naming Series\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Manufacturing Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Education Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Hotel Settings\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"CRM Settings\",\"col\":3}}]",
|
||||
"creation": "2020-03-12 14:47:51.166455",
|
||||
"creation": "2022-01-27 13:14:47.349433",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
@@ -10,7 +10,7 @@
|
||||
"idx": 0,
|
||||
"label": "ERPNext Settings",
|
||||
"links": [],
|
||||
"modified": "2022-01-13 19:18:59.362820",
|
||||
"modified": "2022-04-28 11:42:01.043569",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "ERPNext Settings",
|
||||
@@ -90,20 +90,6 @@
|
||||
"restrict_to_domain": "Manufacturing",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"icon": "education",
|
||||
"label": "Education Settings",
|
||||
"link_to": "Education Settings",
|
||||
"restrict_to_domain": "Education",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"icon": "organization",
|
||||
"label": "Hotel Settings",
|
||||
"link_to": "Hotel Settings",
|
||||
"restrict_to_domain": "Hospitality",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"icon": "setting",
|
||||
"label": "Domain Settings",
|
||||
|
||||
Reference in New Issue
Block a user