Merge branch 'develop' into quality_procedure_fix
This commit is contained in:
commit
6e751440cf
@ -5,7 +5,22 @@ import frappe
|
|||||||
import json
|
import json
|
||||||
from frappe.utils import nowdate, add_months, get_date_str
|
from frappe.utils import nowdate, add_months, get_date_str
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.accounts.utils import get_fiscal_year, get_account_name
|
from erpnext.accounts.utils import get_fiscal_year, get_account_name, FiscalYearError
|
||||||
|
|
||||||
|
def _get_fiscal_year(date=None):
|
||||||
|
try:
|
||||||
|
fiscal_year = get_fiscal_year(date=nowdate(), as_dict=True)
|
||||||
|
return fiscal_year
|
||||||
|
|
||||||
|
except FiscalYearError:
|
||||||
|
#if no fiscal year for current date then get default fiscal year
|
||||||
|
try:
|
||||||
|
fiscal_year = get_fiscal_year(as_dict=True)
|
||||||
|
return fiscal_year
|
||||||
|
|
||||||
|
except FiscalYearError:
|
||||||
|
#if still no fiscal year found then no accounting data created, return
|
||||||
|
return None
|
||||||
|
|
||||||
def get_company_for_dashboards():
|
def get_company_for_dashboards():
|
||||||
company = frappe.defaults.get_defaults().company
|
company = frappe.defaults.get_defaults().company
|
||||||
@ -18,10 +33,16 @@ def get_company_for_dashboards():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
|
|
||||||
|
fiscal_year = _get_fiscal_year(nowdate())
|
||||||
|
|
||||||
|
if not fiscal_year:
|
||||||
|
return frappe._dict()
|
||||||
|
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
"dashboards": get_dashboards(),
|
"dashboards": get_dashboards(),
|
||||||
"charts": get_charts(),
|
"charts": get_charts(fiscal_year),
|
||||||
"number_cards": get_number_cards()
|
"number_cards": get_number_cards(fiscal_year)
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_dashboards():
|
def get_dashboards():
|
||||||
@ -46,10 +67,9 @@ def get_dashboards():
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def get_charts():
|
def get_charts(fiscal_year):
|
||||||
company = frappe.get_doc("Company", get_company_for_dashboards())
|
company = frappe.get_doc("Company", get_company_for_dashboards())
|
||||||
bank_account = company.default_bank_account or get_account_name("Bank", company=company.name)
|
bank_account = company.default_bank_account or get_account_name("Bank", company=company.name)
|
||||||
fiscal_year = get_fiscal_year(date=nowdate())
|
|
||||||
default_cost_center = company.cost_center
|
default_cost_center = company.cost_center
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@ -61,8 +81,8 @@ def get_charts():
|
|||||||
"filters_json": json.dumps({
|
"filters_json": json.dumps({
|
||||||
"company": company.name,
|
"company": company.name,
|
||||||
"filter_based_on": "Fiscal Year",
|
"filter_based_on": "Fiscal Year",
|
||||||
"from_fiscal_year": fiscal_year[0],
|
"from_fiscal_year": fiscal_year.get('name'),
|
||||||
"to_fiscal_year": fiscal_year[0],
|
"to_fiscal_year": fiscal_year.get('name'),
|
||||||
"periodicity": "Monthly",
|
"periodicity": "Monthly",
|
||||||
"include_default_book_entries": 1
|
"include_default_book_entries": 1
|
||||||
}),
|
}),
|
||||||
@ -158,8 +178,8 @@ def get_charts():
|
|||||||
"report_name": "Budget Variance Report",
|
"report_name": "Budget Variance Report",
|
||||||
"filters_json": json.dumps({
|
"filters_json": json.dumps({
|
||||||
"company": company.name,
|
"company": company.name,
|
||||||
"from_fiscal_year": fiscal_year[0],
|
"from_fiscal_year": fiscal_year.get('name'),
|
||||||
"to_fiscal_year": fiscal_year[0],
|
"to_fiscal_year": fiscal_year.get('name'),
|
||||||
"period": "Monthly",
|
"period": "Monthly",
|
||||||
"budget_against": "Cost Center"
|
"budget_against": "Cost Center"
|
||||||
}),
|
}),
|
||||||
@ -190,10 +210,10 @@ def get_charts():
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_number_cards():
|
def get_number_cards(fiscal_year):
|
||||||
fiscal_year = get_fiscal_year(date=nowdate())
|
|
||||||
year_start_date = get_date_str(fiscal_year[1])
|
year_start_date = get_date_str(fiscal_year.get("year_start_date"))
|
||||||
year_end_date = get_date_str(fiscal_year[2])
|
year_end_date = get_date_str(fiscal_year.get("year_end_date"))
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"doctype": "Number Card",
|
"doctype": "Number Card",
|
||||||
|
@ -72,7 +72,11 @@ def make_dimension_in_accounting_doctypes(doc):
|
|||||||
if doctype == "Budget":
|
if doctype == "Budget":
|
||||||
add_dimension_to_budget_doctype(df, doc)
|
add_dimension_to_budget_doctype(df, doc)
|
||||||
else:
|
else:
|
||||||
create_custom_field(doctype, df)
|
meta = frappe.get_meta(doctype, cached=False)
|
||||||
|
fieldnames = [d.fieldname for d in meta.get("fields")]
|
||||||
|
|
||||||
|
if df['fieldname'] not in fieldnames:
|
||||||
|
create_custom_field(doctype, df)
|
||||||
|
|
||||||
count += 1
|
count += 1
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -582,14 +582,14 @@ class SalesInvoice(SellingController):
|
|||||||
|
|
||||||
def validate_item_code(self):
|
def validate_item_code(self):
|
||||||
for d in self.get('items'):
|
for d in self.get('items'):
|
||||||
if not d.item_code:
|
if not d.item_code and self.is_opening == "No":
|
||||||
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
|
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
|
||||||
|
|
||||||
def validate_warehouse(self):
|
def validate_warehouse(self):
|
||||||
super(SalesInvoice, self).validate_warehouse()
|
super(SalesInvoice, self).validate_warehouse()
|
||||||
|
|
||||||
for d in self.get_item_list():
|
for d in self.get_item_list():
|
||||||
if not d.warehouse and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
if not d.warehouse and d.item_code and frappe.get_cached_value("Item", d.item_code, "is_stock_item"):
|
||||||
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
frappe.throw(_("Warehouse required for stock Item {0}").format(d.item_code))
|
||||||
|
|
||||||
def validate_delivery_note(self):
|
def validate_delivery_note(self):
|
||||||
|
@ -1745,53 +1745,6 @@ class TestSalesInvoice(unittest.TestCase):
|
|||||||
|
|
||||||
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
check_gl_entries(self, si.name, expected_gle, "2019-01-30")
|
||||||
|
|
||||||
def test_deferred_error_email(self):
|
|
||||||
deferred_account = create_account(account_name="Deferred Revenue",
|
|
||||||
parent_account="Current Liabilities - _TC", company="_Test Company")
|
|
||||||
|
|
||||||
item = create_item("_Test Item for Deferred Accounting")
|
|
||||||
item.enable_deferred_revenue = 1
|
|
||||||
item.deferred_revenue_account = deferred_account
|
|
||||||
item.no_of_months = 12
|
|
||||||
item.save()
|
|
||||||
|
|
||||||
si = create_sales_invoice(item=item.name, posting_date="2019-01-10", do_not_submit=True)
|
|
||||||
si.items[0].enable_deferred_revenue = 1
|
|
||||||
si.items[0].service_start_date = "2019-01-10"
|
|
||||||
si.items[0].service_end_date = "2019-03-15"
|
|
||||||
si.items[0].deferred_revenue_account = deferred_account
|
|
||||||
si.save()
|
|
||||||
si.submit()
|
|
||||||
|
|
||||||
from erpnext.accounts.deferred_revenue import convert_deferred_revenue_to_income
|
|
||||||
|
|
||||||
acc_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
|
||||||
acc_settings.acc_frozen_upto = '2019-01-31'
|
|
||||||
acc_settings.save()
|
|
||||||
|
|
||||||
pda = frappe.get_doc(dict(
|
|
||||||
doctype='Process Deferred Accounting',
|
|
||||||
posting_date=nowdate(),
|
|
||||||
start_date="2019-01-01",
|
|
||||||
end_date="2019-03-31",
|
|
||||||
type="Income",
|
|
||||||
company="_Test Company"
|
|
||||||
))
|
|
||||||
|
|
||||||
pda.insert()
|
|
||||||
pda.submit()
|
|
||||||
|
|
||||||
email = frappe.db.sql(""" select name from `tabEmail Queue`
|
|
||||||
where message like %(txt)s """, {
|
|
||||||
'txt': "%%%s%%" % "Error while processing deferred accounting for {0}".format(pda.name)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.assertTrue(email)
|
|
||||||
|
|
||||||
acc_settings.load_from_db()
|
|
||||||
acc_settings.acc_frozen_upto = None
|
|
||||||
acc_settings.save()
|
|
||||||
|
|
||||||
def test_inter_company_transaction(self):
|
def test_inter_company_transaction(self):
|
||||||
|
|
||||||
if not frappe.db.exists("Customer", "_Test Internal Customer"):
|
if not frappe.db.exists("Customer", "_Test Internal Customer"):
|
||||||
|
@ -56,9 +56,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, period_start_date, period_
|
|||||||
to_date = add_months(start_date, months_to_add)
|
to_date = add_months(start_date, months_to_add)
|
||||||
start_date = to_date
|
start_date = to_date
|
||||||
|
|
||||||
if to_date == get_first_day(to_date):
|
# Subtract one day from to_date, as it may be first day in next fiscal year or month
|
||||||
# if to_date is the first day, get the last day of previous month
|
to_date = add_days(to_date, -1)
|
||||||
to_date = add_days(to_date, -1)
|
|
||||||
|
|
||||||
if to_date <= year_end_date:
|
if to_date <= year_end_date:
|
||||||
# the normal case
|
# the normal case
|
||||||
@ -406,6 +405,7 @@ def set_gl_entries_by_account(
|
|||||||
FROM `tabDistributed Cost Center`
|
FROM `tabDistributed Cost Center`
|
||||||
WHERE cost_center IN %(cost_center)s
|
WHERE cost_center IN %(cost_center)s
|
||||||
AND parent NOT IN %(cost_center)s
|
AND parent NOT IN %(cost_center)s
|
||||||
|
AND is_cancelled = 0
|
||||||
GROUP BY parent
|
GROUP BY parent
|
||||||
) as DCC_allocation
|
) as DCC_allocation
|
||||||
WHERE company=%(company)s
|
WHERE company=%(company)s
|
||||||
@ -418,6 +418,7 @@ def set_gl_entries_by_account(
|
|||||||
where company=%(company)s
|
where company=%(company)s
|
||||||
{additional_conditions}
|
{additional_conditions}
|
||||||
and posting_date <= %(to_date)s
|
and posting_date <= %(to_date)s
|
||||||
|
and is_cancelled = 0
|
||||||
{distributed_cost_center_query}
|
{distributed_cost_center_query}
|
||||||
order by account, posting_date""".format(
|
order by account, posting_date""".format(
|
||||||
additional_conditions=additional_conditions,
|
additional_conditions=additional_conditions,
|
||||||
|
@ -5,14 +5,23 @@ import frappe
|
|||||||
import json
|
import json
|
||||||
from frappe.utils import nowdate, add_months, get_date_str
|
from frappe.utils import nowdate, add_months, get_date_str
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.dashboard_fixtures import _get_fiscal_year
|
||||||
|
from erpnext.buying.dashboard_fixtures import get_company_for_dashboards
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
|
|
||||||
|
fiscal_year = _get_fiscal_year(nowdate())
|
||||||
|
|
||||||
|
if not fiscal_year:
|
||||||
|
return frappe._dict()
|
||||||
|
|
||||||
|
year_start_date = get_date_str(fiscal_year.get('year_start_date'))
|
||||||
|
year_end_date = get_date_str(fiscal_year.get('year_end_date'))
|
||||||
|
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
"dashboards": get_dashboards(),
|
"dashboards": get_dashboards(),
|
||||||
"charts": get_charts(),
|
"charts": get_charts(fiscal_year, year_start_date, year_end_date),
|
||||||
"number_cards": get_number_cards(),
|
"number_cards": get_number_cards(fiscal_year, year_start_date, year_end_date),
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_dashboards():
|
def get_dashboards():
|
||||||
@ -31,12 +40,7 @@ def get_dashboards():
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year(date=nowdate())
|
def get_charts(fiscal_year, year_start_date, year_end_date):
|
||||||
year_start_date = get_date_str(fiscal_year[1])
|
|
||||||
year_end_date = get_date_str(fiscal_year[2])
|
|
||||||
|
|
||||||
|
|
||||||
def get_charts():
|
|
||||||
company = get_company_for_dashboards()
|
company = get_company_for_dashboards()
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@ -55,8 +59,8 @@ def get_charts():
|
|||||||
"company": company,
|
"company": company,
|
||||||
"status": "In Location",
|
"status": "In Location",
|
||||||
"filter_based_on": "Fiscal Year",
|
"filter_based_on": "Fiscal Year",
|
||||||
"from_fiscal_year": fiscal_year[0],
|
"from_fiscal_year": fiscal_year.get('name'),
|
||||||
"to_fiscal_year": fiscal_year[0],
|
"to_fiscal_year": fiscal_year.get('name'),
|
||||||
"period_start_date": year_start_date,
|
"period_start_date": year_start_date,
|
||||||
"period_end_date": year_end_date,
|
"period_end_date": year_end_date,
|
||||||
"date_based_on": "Purchase Date",
|
"date_based_on": "Purchase Date",
|
||||||
@ -134,7 +138,7 @@ def get_charts():
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_number_cards():
|
def get_number_cards(fiscal_year, year_start_date, year_end_date):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"name": "Total Assets",
|
"name": "Total Assets",
|
||||||
@ -173,13 +177,3 @@ def get_number_cards():
|
|||||||
"doctype": "Number Card"
|
"doctype": "Number Card"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_company_for_dashboards():
|
|
||||||
company = frappe.defaults.get_defaults().company
|
|
||||||
if company:
|
|
||||||
return company
|
|
||||||
else:
|
|
||||||
company_list = frappe.get_list("Company")
|
|
||||||
if company_list:
|
|
||||||
return company_list[0].name
|
|
||||||
return None
|
|
@ -41,7 +41,7 @@ def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, nex
|
|||||||
team_member = frappe.db.get_value('User', assign_to_member, "email")
|
team_member = frappe.db.get_value('User', assign_to_member, "email")
|
||||||
args = {
|
args = {
|
||||||
'doctype' : 'Asset Maintenance',
|
'doctype' : 'Asset Maintenance',
|
||||||
'assign_to' : team_member,
|
'assign_to' : [team_member],
|
||||||
'name' : asset_maintenance_name,
|
'name' : asset_maintenance_name,
|
||||||
'description' : maintenance_task,
|
'description' : maintenance_task,
|
||||||
'date' : next_due_date
|
'date' : next_due_date
|
||||||
|
@ -5,13 +5,24 @@ import frappe
|
|||||||
import json
|
import json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.dashboard_fixtures import _get_fiscal_year
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
|
|
||||||
|
fiscal_year = _get_fiscal_year(nowdate())
|
||||||
|
|
||||||
|
if not fiscal_year:
|
||||||
|
return frappe._dict()
|
||||||
|
|
||||||
|
company = frappe.get_doc("Company", get_company_for_dashboards())
|
||||||
|
fiscal_year_name = fiscal_year.get("name")
|
||||||
|
start_date = str(fiscal_year.get("year_start_date"))
|
||||||
|
end_date = str(fiscal_year.get("year_end_date"))
|
||||||
|
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
"dashboards": get_dashboards(),
|
"dashboards": get_dashboards(),
|
||||||
"charts": get_charts(),
|
"charts": get_charts(company, fiscal_year_name, start_date, end_date),
|
||||||
"number_cards": get_number_cards(),
|
"number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date),
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_company_for_dashboards():
|
def get_company_for_dashboards():
|
||||||
@ -24,12 +35,6 @@ def get_company_for_dashboards():
|
|||||||
return company_list[0].name
|
return company_list[0].name
|
||||||
return None
|
return None
|
||||||
|
|
||||||
company = frappe.get_doc("Company", get_company_for_dashboards())
|
|
||||||
fiscal_year = get_fiscal_year(nowdate(), as_dict=1)
|
|
||||||
fiscal_year_name = fiscal_year.get("name")
|
|
||||||
start_date = str(fiscal_year.get("year_start_date"))
|
|
||||||
end_date = str(fiscal_year.get("year_end_date"))
|
|
||||||
|
|
||||||
def get_dashboards():
|
def get_dashboards():
|
||||||
return [{
|
return [{
|
||||||
"name": "Buying",
|
"name": "Buying",
|
||||||
@ -48,7 +53,7 @@ def get_dashboards():
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def get_charts():
|
def get_charts(company, fiscal_year_name, start_date, end_date):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"name": "Purchase Order Analysis",
|
"name": "Purchase Order Analysis",
|
||||||
@ -139,7 +144,7 @@ def get_charts():
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_number_cards():
|
def get_number_cards(company, fiscal_year_name, start_date, end_date):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"name": "Annual Purchase",
|
"name": "Annual Purchase",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -71,6 +71,15 @@ class PurchaseOrder(BuyingController):
|
|||||||
"compare_fields": [["project", "="], ["item_code", "="],
|
"compare_fields": [["project", "="], ["item_code", "="],
|
||||||
["uom", "="], ["conversion_factor", "="]],
|
["uom", "="], ["conversion_factor", "="]],
|
||||||
"is_child_table": True
|
"is_child_table": True
|
||||||
|
},
|
||||||
|
"Material Request": {
|
||||||
|
"ref_dn_field": "material_request",
|
||||||
|
"compare_fields": [["company", "="]],
|
||||||
|
},
|
||||||
|
"Material Request Item": {
|
||||||
|
"ref_dn_field": "material_request_item",
|
||||||
|
"compare_fields": [["project", "="], ["item_code", "="]],
|
||||||
|
"is_child_table": True
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class TestProcurementTracker(unittest.TestCase):
|
|||||||
def test_result_for_procurement_tracker(self):
|
def test_result_for_procurement_tracker(self):
|
||||||
filters = {
|
filters = {
|
||||||
'company': '_Test Procurement Company',
|
'company': '_Test Procurement Company',
|
||||||
'cost_center': '_Test Cost Center - _TC'
|
'cost_center': 'Main - _TPC'
|
||||||
}
|
}
|
||||||
expected_data = self.generate_expected_data()
|
expected_data = self.generate_expected_data()
|
||||||
report = execute(filters)
|
report = execute(filters)
|
||||||
@ -33,24 +33,27 @@ class TestProcurementTracker(unittest.TestCase):
|
|||||||
country="Pakistan"
|
country="Pakistan"
|
||||||
)).insert()
|
)).insert()
|
||||||
warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
|
warehouse = create_warehouse("_Test Procurement Warehouse", company="_Test Procurement Company")
|
||||||
mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse)
|
mr = make_material_request(company="_Test Procurement Company", warehouse=warehouse, cost_center="Main - _TPC")
|
||||||
po = make_purchase_order(mr.name)
|
po = make_purchase_order(mr.name)
|
||||||
po.supplier = "_Test Supplier"
|
po.supplier = "_Test Supplier"
|
||||||
po.get("items")[0].cost_center = "_Test Cost Center - _TC"
|
po.get("items")[0].cost_center = "Main - _TPC"
|
||||||
po.submit()
|
po.submit()
|
||||||
pr = make_purchase_receipt(po.name)
|
pr = make_purchase_receipt(po.name)
|
||||||
|
pr.get("items")[0].cost_center = "Main - _TPC"
|
||||||
pr.submit()
|
pr.submit()
|
||||||
frappe.db.commit()
|
frappe.db.commit()
|
||||||
date_obj = datetime.date(datetime.now())
|
date_obj = datetime.date(datetime.now())
|
||||||
|
|
||||||
|
po.load_from_db()
|
||||||
|
|
||||||
expected_data = {
|
expected_data = {
|
||||||
"material_request_date": date_obj,
|
"material_request_date": date_obj,
|
||||||
"cost_center": "_Test Cost Center - _TC",
|
"cost_center": "Main - _TPC",
|
||||||
"project": None,
|
"project": None,
|
||||||
"requesting_site": "_Test Procurement Warehouse - _TPC",
|
"requesting_site": "_Test Procurement Warehouse - _TPC",
|
||||||
"requestor": "Administrator",
|
"requestor": "Administrator",
|
||||||
"material_request_no": mr.name,
|
"material_request_no": mr.name,
|
||||||
"description": '_Test Item 1',
|
"item_code": '_Test Item',
|
||||||
"quantity": 10.0,
|
"quantity": 10.0,
|
||||||
"unit_of_measurement": "_Test UOM",
|
"unit_of_measurement": "_Test UOM",
|
||||||
"status": "To Bill",
|
"status": "To Bill",
|
||||||
@ -58,9 +61,9 @@ class TestProcurementTracker(unittest.TestCase):
|
|||||||
"purchase_order": po.name,
|
"purchase_order": po.name,
|
||||||
"supplier": "_Test Supplier",
|
"supplier": "_Test Supplier",
|
||||||
"estimated_cost": 0.0,
|
"estimated_cost": 0.0,
|
||||||
"actual_cost": None,
|
"actual_cost": 0.0,
|
||||||
"purchase_order_amt": 5000.0,
|
"purchase_order_amt": po.net_total,
|
||||||
"purchase_order_amt_in_company_currency": 300000.0,
|
"purchase_order_amt_in_company_currency": po.base_net_total,
|
||||||
"expected_delivery_date": date_obj,
|
"expected_delivery_date": date_obj,
|
||||||
"actual_delivery_date": date_obj
|
"actual_delivery_date": date_obj
|
||||||
}
|
}
|
||||||
|
@ -349,7 +349,7 @@ class BuyingController(StockController):
|
|||||||
})
|
})
|
||||||
|
|
||||||
if not rm.rate:
|
if not rm.rate:
|
||||||
rm.rate = get_valuation_rate(raw_material_data.item_code, self.supplier_warehouse,
|
rm.rate = get_valuation_rate(raw_material_data.rm_item_code, self.supplier_warehouse,
|
||||||
self.doctype, self.name, currency=self.company_currency, company=self.company)
|
self.doctype, self.name, currency=self.company_currency, company=self.company)
|
||||||
|
|
||||||
rm.amount = qty * flt(rm.rate)
|
rm.amount = qty * flt(rm.rate)
|
||||||
|
@ -19,7 +19,8 @@ class QualityInspectionNotSubmittedError(frappe.ValidationError): pass
|
|||||||
class StockController(AccountsController):
|
class StockController(AccountsController):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
super(StockController, self).validate()
|
super(StockController, self).validate()
|
||||||
self.validate_inspection()
|
if not self.get('is_return'):
|
||||||
|
self.validate_inspection()
|
||||||
self.validate_serialized_batch()
|
self.validate_serialized_batch()
|
||||||
self.validate_customer_provided_item()
|
self.validate_customer_provided_item()
|
||||||
|
|
||||||
|
@ -3,11 +3,7 @@ from frappe import _
|
|||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
return {
|
return {
|
||||||
'fieldname': 'prevdoc_docname',
|
'fieldname': 'opportunity',
|
||||||
'non_standard_fieldnames': {
|
|
||||||
'Supplier Quotation': 'opportunity',
|
|
||||||
'Quotation': 'opportunity'
|
|
||||||
},
|
|
||||||
'transactions': [
|
'transactions': [
|
||||||
{
|
{
|
||||||
'items': ['Quotation', 'Supplier Quotation']
|
'items': ['Quotation', 'Supplier Quotation']
|
||||||
|
@ -30,24 +30,32 @@
|
|||||||
"fieldname": "text",
|
"fieldname": "text",
|
||||||
"fieldtype": "Small Text",
|
"fieldtype": "Small Text",
|
||||||
"label": "Tweet",
|
"label": "Tweet",
|
||||||
"mandatory_depends_on": "eval:doc.twitter ==1"
|
"mandatory_depends_on": "eval:doc.twitter ==1",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Attach Image",
|
"fieldtype": "Attach Image",
|
||||||
"label": "Image"
|
"label": "Image",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "twitter",
|
"fieldname": "twitter",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Twitter"
|
"label": "Twitter",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "linkedin",
|
"fieldname": "linkedin",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "LinkedIn"
|
"label": "LinkedIn",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "amended_from",
|
"fieldname": "amended_from",
|
||||||
@ -56,13 +64,17 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Social Media Post",
|
"options": "Social Media Post",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval:doc.twitter ==1",
|
"depends_on": "eval:doc.twitter ==1",
|
||||||
"fieldname": "content",
|
"fieldname": "content",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Twitter"
|
"label": "Twitter",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
@ -70,7 +82,9 @@
|
|||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"label": "Post Status",
|
"label": "Post Status",
|
||||||
"options": "\nScheduled\nPosted\nError",
|
"options": "\nScheduled\nPosted\nError",
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
@ -78,7 +92,9 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "Twitter Post Id",
|
"label": "Twitter Post Id",
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
@ -86,68 +102,89 @@
|
|||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 1,
|
"hidden": 1,
|
||||||
"label": "LinkedIn Post Id",
|
"label": "LinkedIn Post Id",
|
||||||
"read_only": 1
|
"read_only": 1,
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "campaign_name",
|
"fieldname": "campaign_name",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Campaign",
|
"label": "Campaign",
|
||||||
"options": "Campaign"
|
"options": "Campaign",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_6",
|
"fieldname": "column_break_6",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"label": "Share On"
|
"label": "Share On",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_14",
|
"fieldname": "column_break_14",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "tweet_preview",
|
"fieldname": "tweet_preview",
|
||||||
"fieldtype": "HTML"
|
"fieldtype": "HTML",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"depends_on": "eval:doc.linkedin==1",
|
"depends_on": "eval:doc.linkedin==1",
|
||||||
"fieldname": "linkedin_section",
|
"fieldname": "linkedin_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "LinkedIn"
|
"label": "LinkedIn",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"collapsible": 1,
|
"collapsible": 1,
|
||||||
"fieldname": "attachments_section",
|
"fieldname": "attachments_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "Attachments"
|
"label": "Attachments",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "linkedin_post",
|
"fieldname": "linkedin_post",
|
||||||
"fieldtype": "Text",
|
"fieldtype": "Text",
|
||||||
"label": "Post",
|
"label": "Post",
|
||||||
"mandatory_depends_on": "eval:doc.linkedin ==1"
|
"mandatory_depends_on": "eval:doc.linkedin ==1",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_15",
|
"fieldname": "column_break_15",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_on_submit": 1,
|
"allow_on_submit": 1,
|
||||||
"fieldname": "scheduled_time",
|
"fieldname": "scheduled_time",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Scheduled Time",
|
"label": "Scheduled Time",
|
||||||
"read_only_depends_on": "eval:doc.post_status == \"Posted\""
|
"read_only_depends_on": "eval:doc.post_status == \"Posted\"",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-04-21 15:10:04.953713",
|
"modified": "2020-06-14 10:31:33.961381",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "CRM",
|
"module": "CRM",
|
||||||
"name": "Social Media Post",
|
"name": "Social Media Post",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
|
"cancel": 1,
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
@ -157,6 +194,35 @@
|
|||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "System Manager",
|
"role": "System Manager",
|
||||||
"share": 1,
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cancel": 1,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Sales User",
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
|
"write": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cancel": 1,
|
||||||
|
"create": 1,
|
||||||
|
"delete": 1,
|
||||||
|
"email": 1,
|
||||||
|
"export": 1,
|
||||||
|
"print": 1,
|
||||||
|
"read": 1,
|
||||||
|
"report": 1,
|
||||||
|
"role": "Sales Manager",
|
||||||
|
"share": 1,
|
||||||
|
"submit": 1,
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
@ -1,398 +1,119 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_guest_to_view": 1,
|
"allow_guest_to_view": 1,
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "",
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2016-09-13 03:05:27.154713",
|
"creation": "2016-09-13 03:05:27.154713",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "Document",
|
"document_type": "Document",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"title",
|
||||||
|
"route",
|
||||||
|
"column_break_3",
|
||||||
|
"academic_year",
|
||||||
|
"admission_start_date",
|
||||||
|
"admission_end_date",
|
||||||
|
"published",
|
||||||
|
"enable_admission_application",
|
||||||
|
"section_break_5",
|
||||||
|
"program_details",
|
||||||
|
"introduction"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "title",
|
"fieldname": "title",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"label": "Title"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Title",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": "",
|
|
||||||
"fieldname": "route",
|
"fieldname": "route",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"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": "Route",
|
"label": "Route",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": 1
|
"unique": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "application_form_route",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"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": "Application Form Route",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_3",
|
"fieldname": "column_break_3",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column 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,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": "academic_year",
|
"fieldname": "academic_year",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Academic Year",
|
"label": "Academic Year",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Academic Year",
|
"options": "Academic Year",
|
||||||
"permlevel": 0,
|
"reqd": 1
|
||||||
"precision": "",
|
|
||||||
"print_hide": 0,
|
|
||||||
"print_hide_if_no_value": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"remember_last_selected_value": 0,
|
|
||||||
"report_hide": 0,
|
|
||||||
"reqd": 1,
|
|
||||||
"search_index": 0,
|
|
||||||
"set_only_once": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "admission_start_date",
|
"fieldname": "admission_start_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"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": "Admission Start Date",
|
"label": "Admission Start Date",
|
||||||
"length": 0,
|
"no_copy": 1
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": "admission_end_date",
|
"fieldname": "admission_end_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"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": "Admission End Date",
|
"label": "Admission End Date",
|
||||||
"length": 0,
|
"no_copy": 1
|
||||||
"no_copy": 1,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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,
|
"default": "0",
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "published",
|
"fieldname": "published",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 0,
|
"label": "Publish on website"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Publish on website",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": "section_break_5",
|
"fieldname": "section_break_5",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"hidden": 0,
|
"label": "Eligibility and Details"
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Eligibility and Details",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": "program_details",
|
"fieldname": "program_details",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"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": "Eligibility and Details",
|
"label": "Eligibility and Details",
|
||||||
"length": 0,
|
"options": "Student Admission Program"
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Student Admission Program",
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": "introduction",
|
"fieldname": "introduction",
|
||||||
"fieldtype": "Text Editor",
|
"fieldtype": "Text Editor",
|
||||||
"hidden": 0,
|
"label": "Introduction"
|
||||||
"ignore_user_permissions": 0,
|
},
|
||||||
"ignore_xss_filter": 0,
|
{
|
||||||
"in_filter": 0,
|
"default": "0",
|
||||||
"in_global_search": 0,
|
"fieldname": "enable_admission_application",
|
||||||
"in_list_view": 0,
|
"fieldtype": "Check",
|
||||||
"in_standard_filter": 0,
|
"label": "Enable Admission Application"
|
||||||
"label": "Introduction",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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": 1,
|
"has_web_view": 1,
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_published_field": "published",
|
"is_published_field": "published",
|
||||||
"is_submittable": 0,
|
"links": [],
|
||||||
"issingle": 0,
|
"modified": "2020-06-15 20:18:38.591626",
|
||||||
"istable": 0,
|
|
||||||
"max_attachments": 0,
|
|
||||||
"modified": "2017-11-10 18:57:34.570376",
|
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Student Admission",
|
"name": "Student Admission",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [
|
"permissions": [
|
||||||
{
|
{
|
||||||
"amend": 0,
|
|
||||||
"apply_user_permissions": 0,
|
|
||||||
"cancel": 0,
|
|
||||||
"create": 1,
|
"create": 1,
|
||||||
"delete": 1,
|
"delete": 1,
|
||||||
"email": 1,
|
"email": 1,
|
||||||
"export": 1,
|
"export": 1,
|
||||||
"if_owner": 0,
|
|
||||||
"import": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"print": 1,
|
"print": 1,
|
||||||
"read": 1,
|
"read": 1,
|
||||||
"report": 1,
|
"report": 1,
|
||||||
"role": "Academics User",
|
"role": "Academics User",
|
||||||
"set_user_permissions": 0,
|
|
||||||
"share": 1,
|
"share": 1,
|
||||||
"submit": 0,
|
|
||||||
"write": 1
|
"write": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"quick_entry": 0,
|
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"restrict_to_domain": "Education",
|
"restrict_to_domain": "Education",
|
||||||
"route": "admissions",
|
"route": "admissions",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"title_field": "title",
|
"title_field": "title"
|
||||||
"track_changes": 0,
|
|
||||||
"track_seen": 0
|
|
||||||
}
|
}
|
@ -43,8 +43,8 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr class="active">
|
<tr class="active">
|
||||||
<th style="width: 90px">Program/Std.</th>
|
<th style="width: 90px">Program/Std.</th>
|
||||||
<th style="width: 170px">Minumum Age(DOB)</th>
|
<th style="width: 170px">Minumum Age</th>
|
||||||
<th style="width: 170px">Maximum Age(DOB)</th>
|
<th style="width: 170px">Maximum Age</th>
|
||||||
<th style="width: 100px">Application Fee</th>
|
<th style="width: 100px">Application Fee</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -52,8 +52,8 @@
|
|||||||
{% for row in program_details %}
|
{% for row in program_details %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ row.program }}</td>
|
<td>{{ row.program }}</td>
|
||||||
<td>{{ row.minimum_age }}</td>
|
<td>{{ row.min_age }}</td>
|
||||||
<td>{{ row.maximum_age }}</td>
|
<td>{{ row.max_age }}</td>
|
||||||
<td>{{ row.application_fee }}</td>
|
<td>{{ row.application_fee }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
@ -61,12 +61,11 @@
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{%- if doc.enable_admission_application -%}
|
||||||
{%- if application_form_route -%}
|
|
||||||
<br>
|
<br>
|
||||||
<p>
|
<p>
|
||||||
<a class='btn btn-primary'
|
<a class='btn btn-primary'
|
||||||
href='/{{ doc.application_form_route }}?new=1'>
|
href='/student-applicant?new=1&student_admission={{doc.name}}'>
|
||||||
{{ _("Apply Now") }}</a>
|
{{ _("Apply Now") }}</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -11,7 +11,7 @@ QUnit.test('Test: Student Admission', function(assert) {
|
|||||||
{admission_start_date: '2016-04-20'},
|
{admission_start_date: '2016-04-20'},
|
||||||
{admission_end_date: '2016-05-31'},
|
{admission_end_date: '2016-05-31'},
|
||||||
{title: '2016-17 Admissions'},
|
{title: '2016-17 Admissions'},
|
||||||
{application_form_route: 'student-applicant'},
|
{enable_admission_application: 1},
|
||||||
{introduction: 'Test intro'},
|
{introduction: 'Test intro'},
|
||||||
{program_details: [
|
{program_details: [
|
||||||
[
|
[
|
||||||
@ -28,7 +28,7 @@ QUnit.test('Test: Student Admission', function(assert) {
|
|||||||
assert.ok(cur_frm.doc.admission_start_date == '2016-04-20');
|
assert.ok(cur_frm.doc.admission_start_date == '2016-04-20');
|
||||||
assert.ok(cur_frm.doc.admission_end_date == '2016-05-31');
|
assert.ok(cur_frm.doc.admission_end_date == '2016-05-31');
|
||||||
assert.ok(cur_frm.doc.title == '2016-17 Admissions');
|
assert.ok(cur_frm.doc.title == '2016-17 Admissions');
|
||||||
assert.ok(cur_frm.doc.application_form_route == 'student-applicant');
|
assert.ok(cur_frm.doc.enable_admission_application == 1);
|
||||||
assert.ok(cur_frm.doc.introduction == 'Test intro');
|
assert.ok(cur_frm.doc.introduction == 'Test intro');
|
||||||
assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected');
|
assert.ok(cur_frm.doc.program_details[0].program == 'Standard Test', 'Program correctly selected');
|
||||||
assert.ok(cur_frm.doc.program_details[0].application_fee == 1000);
|
assert.ok(cur_frm.doc.program_details[0].application_fee == 1000);
|
||||||
|
@ -1,237 +1,77 @@
|
|||||||
{
|
{
|
||||||
"allow_copy": 0,
|
"actions": [],
|
||||||
"allow_events_in_timeline": 0,
|
|
||||||
"allow_guest_to_view": 0,
|
|
||||||
"allow_import": 0,
|
|
||||||
"allow_rename": 0,
|
|
||||||
"autoname": "",
|
|
||||||
"beta": 0,
|
|
||||||
"creation": "2017-09-15 12:59:43.207923",
|
"creation": "2017-09-15 12:59:43.207923",
|
||||||
"custom": 0,
|
|
||||||
"docstatus": 0,
|
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"document_type": "",
|
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"program",
|
||||||
|
"min_age",
|
||||||
|
"max_age",
|
||||||
|
"column_break_4",
|
||||||
|
"application_fee",
|
||||||
|
"applicant_naming_series"
|
||||||
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "program",
|
"fieldname": "program",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Program",
|
"label": "Program",
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"options": "Program",
|
"options": "Program",
|
||||||
"permlevel": 0,
|
"show_days": 1,
|
||||||
"precision": "",
|
"show_seconds": 1
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "minimum_age",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Minimum Age",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "maximum_age",
|
|
||||||
"fieldtype": "Date",
|
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Maximum Age",
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "column_break_4",
|
"fieldname": "column_break_4",
|
||||||
"fieldtype": "Column Break",
|
"fieldtype": "Column Break",
|
||||||
"hidden": 0,
|
"show_days": 1,
|
||||||
"ignore_user_permissions": 0,
|
"show_seconds": 1
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 0,
|
|
||||||
"in_standard_filter": 0,
|
|
||||||
"length": 0,
|
|
||||||
"no_copy": 0,
|
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "application_fee",
|
"fieldname": "application_fee",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Application Fee",
|
"label": "Application Fee",
|
||||||
"length": 0,
|
"show_days": 1,
|
||||||
"no_copy": 0,
|
"show_seconds": 1
|
||||||
"permlevel": 0,
|
|
||||||
"precision": "",
|
|
||||||
"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,
|
|
||||||
"translatable": 0,
|
|
||||||
"unique": 0
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 0,
|
|
||||||
"allow_in_quick_entry": 0,
|
|
||||||
"allow_on_submit": 0,
|
|
||||||
"bold": 0,
|
|
||||||
"collapsible": 0,
|
|
||||||
"columns": 0,
|
|
||||||
"fieldname": "applicant_naming_series",
|
"fieldname": "applicant_naming_series",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
|
||||||
"ignore_user_permissions": 0,
|
|
||||||
"ignore_xss_filter": 0,
|
|
||||||
"in_filter": 0,
|
|
||||||
"in_global_search": 0,
|
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 0,
|
|
||||||
"label": "Naming Series (for Student Applicant)",
|
"label": "Naming Series (for Student Applicant)",
|
||||||
"length": 0,
|
"show_days": 1,
|
||||||
"no_copy": 0,
|
"show_seconds": 1
|
||||||
"permlevel": 0,
|
},
|
||||||
"precision": "",
|
{
|
||||||
"print_hide": 0,
|
"fieldname": "min_age",
|
||||||
"print_hide_if_no_value": 0,
|
"fieldtype": "Int",
|
||||||
"read_only": 0,
|
"in_list_view": 1,
|
||||||
"remember_last_selected_value": 0,
|
"label": "Minimum Age",
|
||||||
"report_hide": 0,
|
"show_days": 1,
|
||||||
"reqd": 0,
|
"show_seconds": 1
|
||||||
"search_index": 0,
|
},
|
||||||
"set_only_once": 0,
|
{
|
||||||
"translatable": 0,
|
"fieldname": "max_age",
|
||||||
"unique": 0
|
"fieldtype": "Int",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Maximum Age",
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"has_web_view": 0,
|
|
||||||
"hide_heading": 0,
|
|
||||||
"hide_toolbar": 0,
|
|
||||||
"idx": 0,
|
|
||||||
"image_view": 0,
|
|
||||||
"in_create": 0,
|
|
||||||
"is_submittable": 0,
|
|
||||||
"issingle": 0,
|
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"max_attachments": 0,
|
"links": [],
|
||||||
"modified": "2018-11-04 03:37:17.408427",
|
"modified": "2020-06-10 23:06:30.037404",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "Student Admission Program",
|
"name": "Student Admission Program",
|
||||||
"name_case": "",
|
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
"quick_entry": 1,
|
"quick_entry": 1,
|
||||||
"read_only": 0,
|
|
||||||
"read_only_onload": 0,
|
|
||||||
"restrict_to_domain": "Education",
|
"restrict_to_domain": "Education",
|
||||||
"show_name_in_global_search": 0,
|
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1,
|
"track_changes": 1
|
||||||
"track_seen": 0,
|
|
||||||
"track_views": 0
|
|
||||||
}
|
}
|
@ -6,7 +6,7 @@ from __future__ import print_function, unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate, add_years, nowdate, date_diff
|
||||||
|
|
||||||
class StudentApplicant(Document):
|
class StudentApplicant(Document):
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
@ -31,6 +31,7 @@ class StudentApplicant(Document):
|
|||||||
def validate(self):
|
def validate(self):
|
||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
|
self.title = " ".join(filter(None, [self.first_name, self.middle_name, self.last_name]))
|
||||||
|
|
||||||
if self.student_admission and self.program and self.date_of_birth:
|
if self.student_admission and self.program and self.date_of_birth:
|
||||||
self.validation_from_student_admission()
|
self.validation_from_student_admission()
|
||||||
|
|
||||||
@ -48,16 +49,16 @@ class StudentApplicant(Document):
|
|||||||
frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant"))
|
frappe.throw(_("Please select Student Admission which is mandatory for the paid student applicant"))
|
||||||
|
|
||||||
def validation_from_student_admission(self):
|
def validation_from_student_admission(self):
|
||||||
|
|
||||||
student_admission = get_student_admission_data(self.student_admission, self.program)
|
student_admission = get_student_admission_data(self.student_admission, self.program)
|
||||||
|
|
||||||
# different validation for minimum and maximum age so that either min/max can also work independently.
|
if student_admission and student_admission.min_age and \
|
||||||
if student_admission and student_admission.minimum_age and \
|
date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.min_age)) < 0:
|
||||||
getdate(student_admission.minimum_age) < getdate(self.date_of_birth):
|
frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth"))
|
||||||
frappe.throw(_("Not eligible for the admission in this program as per DOB"))
|
|
||||||
|
|
||||||
if student_admission and student_admission.maximum_age and \
|
if student_admission and student_admission.max_age and \
|
||||||
getdate(student_admission.maximum_age) > getdate(self.date_of_birth):
|
date_diff(nowdate(), add_years(getdate(self.date_of_birth), student_admission.max_age)) > 0:
|
||||||
frappe.throw(_("Not eligible for the admission in this program as per DOB"))
|
frappe.throw(_("Not eligible for the admission in this program as per Date Of Birth"))
|
||||||
|
|
||||||
|
|
||||||
def on_payment_authorized(self, *args, **kwargs):
|
def on_payment_authorized(self, *args, **kwargs):
|
||||||
@ -65,10 +66,12 @@ class StudentApplicant(Document):
|
|||||||
|
|
||||||
|
|
||||||
def get_student_admission_data(student_admission, program):
|
def get_student_admission_data(student_admission, program):
|
||||||
|
|
||||||
student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date,
|
student_admission = frappe.db.sql("""select sa.admission_start_date, sa.admission_end_date,
|
||||||
sap.program, sap.minimum_age, sap.maximum_age, sap.applicant_naming_series
|
sap.program, sap.min_age, sap.max_age, sap.applicant_naming_series
|
||||||
from `tabStudent Admission` sa, `tabStudent Admission Program` sap
|
from `tabStudent Admission` sa, `tabStudent Admission Program` sap
|
||||||
where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1)
|
where sa.name = sap.parent and sa.name = %s and sap.program = %s""", (student_admission, program), as_dict=1)
|
||||||
|
|
||||||
if student_admission:
|
if student_admission:
|
||||||
return student_admission[0]
|
return student_admission[0]
|
||||||
else:
|
else:
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"login_required": 1,
|
"login_required": 1,
|
||||||
"max_attachment_size": 0,
|
"max_attachment_size": 0,
|
||||||
"modified": "2017-02-21 05:44:46.022738",
|
"modified": "2020-06-11 22:53:45.875310",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Education",
|
"module": "Education",
|
||||||
"name": "student-applicant",
|
"name": "student-applicant",
|
||||||
@ -24,12 +24,16 @@
|
|||||||
"payment_button_label": "Buy Now",
|
"payment_button_label": "Buy Now",
|
||||||
"published": 1,
|
"published": 1,
|
||||||
"route": "student-applicant",
|
"route": "student-applicant",
|
||||||
|
"route_to_success_link": 0,
|
||||||
|
"show_attachments": 0,
|
||||||
|
"show_in_grid": 0,
|
||||||
"show_sidebar": 1,
|
"show_sidebar": 1,
|
||||||
"sidebar_items": [],
|
"sidebar_items": [],
|
||||||
"success_url": "/student-applicant",
|
"success_url": "/student-applicant",
|
||||||
"title": "Student Applicant",
|
"title": "Student Applicant",
|
||||||
"web_form_fields": [
|
"web_form_fields": [
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "first_name",
|
"fieldname": "first_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -37,9 +41,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "middle_name",
|
"fieldname": "middle_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -47,9 +53,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "last_name",
|
"fieldname": "last_name",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -57,9 +65,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "image",
|
"fieldname": "image",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -67,9 +77,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "program",
|
"fieldname": "program",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -78,9 +90,11 @@
|
|||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "Program",
|
"options": "Program",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 1
|
"reqd": 1,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "academic_year",
|
"fieldname": "academic_year",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -89,9 +103,11 @@
|
|||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "Academic Year",
|
"options": "Academic Year",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "date_of_birth",
|
"fieldname": "date_of_birth",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -99,9 +115,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "blood_group",
|
"fieldname": "blood_group",
|
||||||
"fieldtype": "Select",
|
"fieldtype": "Select",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -110,9 +128,11 @@
|
|||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-",
|
"options": "\nA+\nA-\nB+\nB-\nO+\nO-\nAB+\nAB-",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "student_email_id",
|
"fieldname": "student_email_id",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -120,9 +140,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "student_mobile_number",
|
"fieldname": "student_mobile_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -130,9 +152,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"default": "INDIAN",
|
"default": "INDIAN",
|
||||||
"fieldname": "nationality",
|
"fieldname": "nationality",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
@ -142,9 +166,11 @@
|
|||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "",
|
"options": "",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "address_line_1",
|
"fieldname": "address_line_1",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -152,9 +178,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "address_line_2",
|
"fieldname": "address_line_2",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -162,9 +190,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "pincode",
|
"fieldname": "pincode",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -172,9 +202,11 @@
|
|||||||
"max_length": 0,
|
"max_length": 0,
|
||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "guardians",
|
"fieldname": "guardians",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -183,9 +215,11 @@
|
|||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "Student Guardian",
|
"options": "Student Guardian",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
"fieldname": "siblings",
|
"fieldname": "siblings",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -194,7 +228,21 @@
|
|||||||
"max_value": 0,
|
"max_value": 0,
|
||||||
"options": "Student Sibling",
|
"options": "Student Sibling",
|
||||||
"read_only": 0,
|
"read_only": 0,
|
||||||
"reqd": 0
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_read_on_all_link_options": 0,
|
||||||
|
"fieldname": "student_admission",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"hidden": 0,
|
||||||
|
"label": "Student Admission",
|
||||||
|
"max_length": 0,
|
||||||
|
"max_value": 0,
|
||||||
|
"options": "Student Admission",
|
||||||
|
"read_only": 0,
|
||||||
|
"reqd": 0,
|
||||||
|
"show_in_filter": 0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -15,6 +15,7 @@ class TestInpatientRecord(unittest.TestCase):
|
|||||||
patient = create_patient()
|
patient = create_patient()
|
||||||
# Schedule Admission
|
# Schedule Admission
|
||||||
ip_record = create_inpatient(patient)
|
ip_record = create_inpatient(patient)
|
||||||
|
ip_record.expected_length_of_stay = 0
|
||||||
ip_record.save(ignore_permissions = True)
|
ip_record.save(ignore_permissions = True)
|
||||||
self.assertEqual(ip_record.name, frappe.db.get_value("Patient", patient, "inpatient_record"))
|
self.assertEqual(ip_record.name, frappe.db.get_value("Patient", patient, "inpatient_record"))
|
||||||
self.assertEqual(ip_record.status, frappe.db.get_value("Patient", patient, "inpatient_status"))
|
self.assertEqual(ip_record.status, frappe.db.get_value("Patient", patient, "inpatient_status"))
|
||||||
@ -26,7 +27,7 @@ class TestInpatientRecord(unittest.TestCase):
|
|||||||
self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
|
self.assertEqual("Occupied", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
|
||||||
|
|
||||||
# Discharge
|
# Discharge
|
||||||
schedule_discharge(patient=patient)
|
schedule_discharge(frappe.as_json({'patient': patient}))
|
||||||
self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
|
self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
|
||||||
|
|
||||||
ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
|
ip_record1 = frappe.get_doc("Inpatient Record", ip_record.name)
|
||||||
@ -44,8 +45,10 @@ class TestInpatientRecord(unittest.TestCase):
|
|||||||
patient = create_patient()
|
patient = create_patient()
|
||||||
|
|
||||||
ip_record = create_inpatient(patient)
|
ip_record = create_inpatient(patient)
|
||||||
|
ip_record.expected_length_of_stay = 0
|
||||||
ip_record.save(ignore_permissions = True)
|
ip_record.save(ignore_permissions = True)
|
||||||
ip_record_new = create_inpatient(patient)
|
ip_record_new = create_inpatient(patient)
|
||||||
|
ip_record_new.expected_length_of_stay = 0
|
||||||
self.assertRaises(frappe.ValidationError, ip_record_new.save)
|
self.assertRaises(frappe.ValidationError, ip_record_new.save)
|
||||||
|
|
||||||
service_unit = get_healthcare_service_unit()
|
service_unit = get_healthcare_service_unit()
|
||||||
|
@ -93,7 +93,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "HR",
|
"label": "HR",
|
||||||
"modified": "2020-06-10 12:41:41.695669",
|
"modified": "2020-06-16 19:20:50.976045",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "HR",
|
"name": "HR",
|
||||||
@ -126,7 +126,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Salary Structure",
|
"label": "Salary Structure",
|
||||||
"link_to": "Payroll Entry",
|
"link_to": "Salary Structure",
|
||||||
"type": "DocType"
|
"type": "DocType"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -205,7 +205,7 @@
|
|||||||
"label": "Status",
|
"label": "Status",
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "\nActive\nLeft",
|
"options": "Active\nLeft",
|
||||||
"reqd": 1,
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
@ -667,6 +667,7 @@
|
|||||||
"oldfieldtype": "Date"
|
"oldfieldtype": "Date"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:doc.status == \"Left\"",
|
||||||
"fieldname": "relieving_date",
|
"fieldname": "relieving_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
"label": "Relieving Date",
|
"label": "Relieving Date",
|
||||||
@ -803,7 +804,7 @@
|
|||||||
"idx": 24,
|
"idx": 24,
|
||||||
"image_field": "image",
|
"image_field": "image",
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-05-05 18:51:03.152503",
|
"modified": "2020-06-15 12:26:30.003741",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Employee",
|
"name": "Employee",
|
||||||
|
@ -66,6 +66,7 @@
|
|||||||
"fieldname": "employee",
|
"fieldname": "employee",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_global_search": 1,
|
"in_global_search": 1,
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "From Employee",
|
"label": "From Employee",
|
||||||
"oldfieldname": "employee",
|
"oldfieldname": "employee",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
@ -164,6 +165,7 @@
|
|||||||
"default": "Today",
|
"default": "Today",
|
||||||
"fieldname": "posting_date",
|
"fieldname": "posting_date",
|
||||||
"fieldtype": "Date",
|
"fieldtype": "Date",
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Posting Date",
|
"label": "Posting Date",
|
||||||
"oldfieldname": "posting_date",
|
"oldfieldname": "posting_date",
|
||||||
"oldfieldtype": "Date",
|
"oldfieldtype": "Date",
|
||||||
@ -236,6 +238,7 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "company",
|
"fieldname": "company",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
|
"in_standard_filter": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"oldfieldname": "company",
|
"oldfieldname": "company",
|
||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
@ -368,7 +371,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2019-12-14 23:52:05.388458",
|
"modified": "2020-06-15 12:43:04.099803",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "HR",
|
"module": "HR",
|
||||||
"name": "Expense Claim",
|
"name": "Expense Claim",
|
||||||
|
@ -46,6 +46,7 @@ frappe.ui.form.on("Leave Application", {
|
|||||||
|
|
||||||
make_dashboard: function(frm) {
|
make_dashboard: function(frm) {
|
||||||
var leave_details;
|
var leave_details;
|
||||||
|
let lwps;
|
||||||
if (frm.doc.employee) {
|
if (frm.doc.employee) {
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
|
method: "erpnext.hr.doctype.leave_application.leave_application.get_leave_details",
|
||||||
@ -61,6 +62,7 @@ frappe.ui.form.on("Leave Application", {
|
|||||||
if (!r.exc && r.message['leave_approver']) {
|
if (!r.exc && r.message['leave_approver']) {
|
||||||
frm.set_value('leave_approver', r.message['leave_approver']);
|
frm.set_value('leave_approver', r.message['leave_approver']);
|
||||||
}
|
}
|
||||||
|
lwps = r.message["lwps"];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$("div").remove(".form-dashboard-section.custom");
|
$("div").remove(".form-dashboard-section.custom");
|
||||||
@ -70,12 +72,17 @@ frappe.ui.form.on("Leave Application", {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
frm.dashboard.show();
|
frm.dashboard.show();
|
||||||
|
let allowed_leave_types = Object.keys(leave_details);
|
||||||
|
|
||||||
|
// lwps should be allowed, lwps don't have any allocation
|
||||||
|
allowed_leave_types = allowed_leave_types.concat(lwps);
|
||||||
|
|
||||||
frm.set_query('leave_type', function(){
|
frm.set_query('leave_type', function(){
|
||||||
return {
|
return {
|
||||||
filters : [
|
filters : [
|
||||||
['leave_type_name', 'in', Object.keys(leave_details)]
|
['leave_type_name', 'in', allowed_leave_types]
|
||||||
]
|
]
|
||||||
}
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -19,7 +19,6 @@ class NotAnOptionalHoliday(frappe.ValidationError): pass
|
|||||||
|
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
class LeaveApplication(Document):
|
class LeaveApplication(Document):
|
||||||
|
|
||||||
def get_feed(self):
|
def get_feed(self):
|
||||||
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
|
return _("{0}: From {0} of type {1}").format(self.employee_name, self.leave_type)
|
||||||
|
|
||||||
@ -463,9 +462,14 @@ def get_leave_details(employee, date):
|
|||||||
"pending_leaves": leaves_pending,
|
"pending_leaves": leaves_pending,
|
||||||
"remaining_leaves": remaining_leaves}
|
"remaining_leaves": remaining_leaves}
|
||||||
|
|
||||||
|
#is used in set query
|
||||||
|
lwps = frappe.get_list("Leave Type", filters = {"is_lwp": 1})
|
||||||
|
lwps = [lwp.name for lwp in lwps]
|
||||||
|
|
||||||
ret = {
|
ret = {
|
||||||
'leave_allocation': leave_allocation,
|
'leave_allocation': leave_allocation,
|
||||||
'leave_approver': get_leave_approver(employee)
|
'leave_approver': get_leave_approver(employee),
|
||||||
|
'lwps': lwps
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
import frappe, erpnext, json
|
import frappe, erpnext, json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import nowdate, get_first_day, get_last_day, add_months
|
from frappe.utils import nowdate, get_first_day, get_last_day, add_months
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
|
@ -119,6 +119,7 @@ class BOM(WebsiteGenerator):
|
|||||||
"description": d.description,
|
"description": d.description,
|
||||||
"time_in_mins": d.time_in_mins,
|
"time_in_mins": d.time_in_mins,
|
||||||
"batch_size": d.batch_size,
|
"batch_size": d.batch_size,
|
||||||
|
"operating_cost": d.operating_cost,
|
||||||
"idx": d.idx
|
"idx": d.idx
|
||||||
})
|
})
|
||||||
child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2)
|
child.hour_rate = flt(d.hour_rate / self.conversion_rate, 2)
|
||||||
|
@ -78,6 +78,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval:parent.doctype == 'BOM'",
|
||||||
"fieldname": "base_hour_rate",
|
"fieldname": "base_hour_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Base Hour Rate(Company Currency)",
|
"label": "Base Hour Rate(Company Currency)",
|
||||||
@ -87,6 +88,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "5",
|
"default": "5",
|
||||||
|
"depends_on": "eval:parent.doctype == 'BOM'",
|
||||||
"fieldname": "base_operating_cost",
|
"fieldname": "base_operating_cost",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Operating Cost(Company Currency)",
|
"label": "Operating Cost(Company Currency)",
|
||||||
@ -108,8 +110,8 @@
|
|||||||
],
|
],
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-07-16 22:35:55.374037",
|
"modified": "2020-06-16 17:01:11.128420",
|
||||||
"modified_by": "govindsmenokee@gmail.com",
|
"modified_by": "Administrator",
|
||||||
"module": "Manufacturing",
|
"module": "Manufacturing",
|
||||||
"name": "BOM Operation",
|
"name": "BOM Operation",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
|
@ -44,7 +44,6 @@ frappe.ui.form.on('BOM Operation', {
|
|||||||
name: d.workstation
|
name: d.workstation
|
||||||
},
|
},
|
||||||
callback: function (data) {
|
callback: function (data) {
|
||||||
frappe.model.set_value(d.doctype, d.name, "base_hour_rate", data.message.hour_rate);
|
|
||||||
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate);
|
||||||
frm.events.calculate_operating_cost(frm, d);
|
frm.events.calculate_operating_cost(frm, d);
|
||||||
}
|
}
|
||||||
|
@ -77,6 +77,7 @@ def create_customer(user_details):
|
|||||||
customer = frappe.new_doc("Customer")
|
customer = frappe.new_doc("Customer")
|
||||||
customer.customer_name = user_details.fullname
|
customer.customer_name = user_details.fullname
|
||||||
customer.customer_type = "Individual"
|
customer.customer_type = "Individual"
|
||||||
|
customer.flags.ignore_mandatory = True
|
||||||
customer.insert(ignore_permissions=True)
|
customer.insert(ignore_permissions=True)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -91,7 +92,11 @@ def create_customer(user_details):
|
|||||||
"link_name": customer.name
|
"link_name": customer.name
|
||||||
})
|
})
|
||||||
|
|
||||||
contact.insert()
|
contact.save()
|
||||||
|
|
||||||
|
except frappe.DuplicateEntryError:
|
||||||
|
return customer.name
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed"))
|
frappe.log_error(frappe.get_traceback(), _("Contact Creation Failed"))
|
||||||
pass
|
pass
|
||||||
|
@ -62,7 +62,10 @@ def get_member_based_on_subscription(subscription_id, email):
|
|||||||
'subscription_id': subscription_id,
|
'subscription_id': subscription_id,
|
||||||
'email_id': email
|
'email_id': email
|
||||||
}, order_by="creation desc")
|
}, order_by="creation desc")
|
||||||
return frappe.get_doc("Member", members[0]['name'])
|
try:
|
||||||
|
return frappe.get_doc("Member", members[0]['name'])
|
||||||
|
except:
|
||||||
|
return None
|
||||||
|
|
||||||
def verify_signature(data):
|
def verify_signature(data):
|
||||||
signature = frappe.request.headers.get('X-Razorpay-Signature')
|
signature = frappe.request.headers.get('X-Razorpay-Signature')
|
||||||
@ -77,7 +80,7 @@ def verify_signature(data):
|
|||||||
|
|
||||||
@frappe.whitelist(allow_guest=True)
|
@frappe.whitelist(allow_guest=True)
|
||||||
def trigger_razorpay_subscription(*args, **kwargs):
|
def trigger_razorpay_subscription(*args, **kwargs):
|
||||||
data = frappe.request.get_data()
|
data = frappe.request.get_data(as_text=True)
|
||||||
verify_signature(data)
|
verify_signature(data)
|
||||||
|
|
||||||
if isinstance(data, six.string_types):
|
if isinstance(data, six.string_types):
|
||||||
@ -96,7 +99,10 @@ def trigger_razorpay_subscription(*args, **kwargs):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed"))
|
error_log = frappe.log_error(frappe.get_traceback() + '\n' + data_json , _("Membership Webhook Failed"))
|
||||||
notify_failure(error_log)
|
notify_failure(error_log)
|
||||||
raise e
|
return False
|
||||||
|
|
||||||
|
if not member:
|
||||||
|
return False
|
||||||
|
|
||||||
if data.event == "subscription.activated":
|
if data.event == "subscription.activated":
|
||||||
member.customer_id = payment.customer_id
|
member.customer_id = payment.customer_id
|
||||||
|
@ -680,6 +680,7 @@ erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
|
|||||||
erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
|
erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
|
||||||
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
|
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
|
||||||
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
|
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
|
||||||
|
execute:frappe.reload_doc("HR", "doctype", "Employee Advance")
|
||||||
erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount
|
erpnext.patches.v12_0.move_due_advance_amount_to_pending_amount
|
||||||
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
|
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
|
||||||
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
|
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
|
||||||
@ -696,3 +697,5 @@ execute:frappe.rename_doc("Desk Page", "Loan Management", "Loan", force=True)
|
|||||||
erpnext.patches.v12_0.update_uom_conversion_factor
|
erpnext.patches.v12_0.update_uom_conversion_factor
|
||||||
erpnext.patches.v13_0.delete_old_purchase_reports
|
erpnext.patches.v13_0.delete_old_purchase_reports
|
||||||
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
|
erpnext.patches.v12_0.set_italian_import_supplier_invoice_permissions
|
||||||
|
erpnext.patches.v13_0.update_sla_enhancements
|
||||||
|
erpnext.patches.v12_0.update_address_template_for_india
|
||||||
|
@ -6,4 +6,6 @@ import frappe
|
|||||||
|
|
||||||
def execute():
|
def execute():
|
||||||
''' Move from due_advance_amount to pending_amount '''
|
''' Move from due_advance_amount to pending_amount '''
|
||||||
frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''')
|
|
||||||
|
if frappe.db.has_column("Employee Advance", "due_advance_amount"):
|
||||||
|
frappe.db.sql(''' UPDATE `tabEmployee Advance` SET pending_amount=due_advance_amount ''')
|
||||||
|
12
erpnext/patches/v12_0/update_address_template_for_india.py
Normal file
12
erpnext/patches/v12_0/update_address_template_for_india.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# Copyright (c) 2020, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
import frappe
|
||||||
|
from erpnext.regional.address_template.setup import set_up_address_templates
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
if frappe.db.get_value('Company', {'country': 'India'}, 'name'):
|
||||||
|
address_template = frappe.db.get_value('Address Template', 'India', 'template')
|
||||||
|
if not address_template or "gstin" not in address_template:
|
||||||
|
set_up_address_templates(default_country='India')
|
93
erpnext/patches/v13_0/update_sla_enhancements.py
Normal file
93
erpnext/patches/v13_0/update_sla_enhancements.py
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
# Copyright (c) 2018, Frappe and Contributors
|
||||||
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import frappe
|
||||||
|
|
||||||
|
def execute():
|
||||||
|
# add holiday list and employee group fields in SLA
|
||||||
|
# change response and resolution time in priorities child table
|
||||||
|
if frappe.db.exists('DocType', 'Service Level Agreement'):
|
||||||
|
sla_details = frappe.db.get_all('Service Level Agreement', fields=['name', 'service_level'])
|
||||||
|
priorities = frappe.db.get_all('Service Level Priority', fields=['*'], filters={
|
||||||
|
'parenttype': ('in', ['Service Level Agreement', 'Service Level'])
|
||||||
|
})
|
||||||
|
|
||||||
|
frappe.reload_doc('support', 'doctype', 'service_level_agreement')
|
||||||
|
frappe.reload_doc('support', 'doctype', 'pause_sla_on_status')
|
||||||
|
frappe.reload_doc('support', 'doctype', 'service_level_priority')
|
||||||
|
frappe.reload_doc('support', 'doctype', 'service_day')
|
||||||
|
|
||||||
|
for entry in sla_details:
|
||||||
|
values = frappe.db.get_value('Service Level', entry.service_level, ['holiday_list', 'employee_group'])
|
||||||
|
if values:
|
||||||
|
holiday_list = values[0]
|
||||||
|
employee_group = values[1]
|
||||||
|
frappe.db.set_value('Service Level Agreement', entry.name, {
|
||||||
|
'holiday_list': holiday_list,
|
||||||
|
'employee_group': employee_group
|
||||||
|
})
|
||||||
|
|
||||||
|
priority_dict = {}
|
||||||
|
|
||||||
|
for priority in priorities:
|
||||||
|
if priority.parenttype == 'Service Level Agreement':
|
||||||
|
response_time = convert_to_seconds(priority.response_time, priority.response_time_period)
|
||||||
|
resolution_time = convert_to_seconds(priority.resolution_time, priority.resolution_time_period)
|
||||||
|
frappe.db.set_value('Service Level Priority', priority.name, {
|
||||||
|
'response_time': response_time,
|
||||||
|
'resolution_time': resolution_time
|
||||||
|
})
|
||||||
|
if priority.parenttype == 'Service Level':
|
||||||
|
if not priority.parent in priority_dict:
|
||||||
|
priority_dict[priority.parent] = []
|
||||||
|
priority_dict[priority.parent].append(priority)
|
||||||
|
|
||||||
|
|
||||||
|
# copy Service Levels to Service Level Agreements
|
||||||
|
sl = [entry.service_level for entry in sla_details]
|
||||||
|
if frappe.db.exists('DocType', 'Service Level'):
|
||||||
|
service_levels = frappe.db.get_all('Service Level', filters={'service_level': ('not in', sl)}, fields=['*'])
|
||||||
|
for entry in service_levels:
|
||||||
|
sla = frappe.new_doc('Service Level Agreement')
|
||||||
|
sla.service_level = entry.service_level
|
||||||
|
sla.holiday_list = entry.holiday_list
|
||||||
|
sla.employee_group = entry.employee_group
|
||||||
|
sla.flags.ignore_validate = True
|
||||||
|
sla = sla.insert(ignore_mandatory=True)
|
||||||
|
|
||||||
|
frappe.db.sql("""
|
||||||
|
UPDATE
|
||||||
|
`tabService Day`
|
||||||
|
SET
|
||||||
|
parent = %(new_parent)s , parentfield = 'support_and_resolution', parenttype = 'Service Level Agreement'
|
||||||
|
WHERE
|
||||||
|
parent = %(old_parent)s
|
||||||
|
""", {'new_parent': sla.name, 'old_parent': entry.name}, as_dict = 1)
|
||||||
|
|
||||||
|
priority_list = priority_dict.get(entry.name)
|
||||||
|
if priority_list:
|
||||||
|
sla = frappe.get_doc('Service Level Agreement', sla.name)
|
||||||
|
for priority in priority_list:
|
||||||
|
row = sla.append('priorities', {
|
||||||
|
'priority': priority.priority,
|
||||||
|
'default_priority': priority.default_priority,
|
||||||
|
'response_time': convert_to_seconds(priority.response_time, priority.response_time_period),
|
||||||
|
'resolution_time': convert_to_seconds(priority.resolution_time, priority.resolution_time_period)
|
||||||
|
})
|
||||||
|
row.db_update()
|
||||||
|
sla.db_update()
|
||||||
|
|
||||||
|
frappe.delete_doc_if_exists('DocType', 'Service Level')
|
||||||
|
|
||||||
|
|
||||||
|
def convert_to_seconds(value, unit):
|
||||||
|
seconds = 0
|
||||||
|
if unit == "Hour":
|
||||||
|
seconds = value * 3600
|
||||||
|
if unit == "Day":
|
||||||
|
seconds = value * 3600 * 24
|
||||||
|
if unit == "Week":
|
||||||
|
seconds = value * 3600 * 24 * 7
|
||||||
|
return seconds
|
@ -1,4 +1,5 @@
|
|||||||
import frappe
|
import frappe
|
||||||
|
from frappe.utils import cint
|
||||||
from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager
|
from erpnext.portal.product_configurator.item_variants_cache import ItemVariantsCacheManager
|
||||||
|
|
||||||
def get_field_filter_data():
|
def get_field_filter_data():
|
||||||
@ -243,6 +244,8 @@ def get_next_attribute_and_values(item_code, selected_attributes):
|
|||||||
else:
|
else:
|
||||||
product_info = None
|
product_info = None
|
||||||
|
|
||||||
|
product_info["allow_items_not_in_stock"] = cint(data.cart_settings.allow_items_not_in_stock)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'next_attribute': next_attribute,
|
'next_attribute': next_attribute,
|
||||||
'valid_options_for_attributes': valid_options_for_attributes,
|
'valid_options_for_attributes': valid_options_for_attributes,
|
||||||
|
@ -64,7 +64,7 @@ class TestTask(unittest.TestCase):
|
|||||||
def assign():
|
def assign():
|
||||||
from frappe.desk.form import assign_to
|
from frappe.desk.form import assign_to
|
||||||
assign_to.add({
|
assign_to.add({
|
||||||
"assign_to": "test@example.com",
|
"assign_to": ["test@example.com"],
|
||||||
"doctype": task.doctype,
|
"doctype": task.doctype,
|
||||||
"name": task.name,
|
"name": task.name,
|
||||||
"description": "Close this task"
|
"description": "Close this task"
|
||||||
|
@ -13,7 +13,7 @@ from erpnext.projects.doctype.timesheet.timesheet import make_salary_slip, make_
|
|||||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
from erpnext.hr.doctype.salary_structure.test_salary_structure \
|
from erpnext.hr.doctype.salary_structure.test_salary_structure \
|
||||||
import make_salary_structure, create_salary_structure_assignment
|
import make_salary_structure, create_salary_structure_assignment
|
||||||
|
from erpnext.hr.doctype.employee.test_employee import make_employee
|
||||||
|
|
||||||
class TestTimesheet(unittest.TestCase):
|
class TestTimesheet(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -25,8 +25,10 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
|
|
||||||
|
|
||||||
def test_timesheet_billing_amount(self):
|
def test_timesheet_billing_amount(self):
|
||||||
make_salary_structure_for_timesheet("_T-Employee-00001")
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1)
|
|
||||||
|
make_salary_structure_for_timesheet(emp)
|
||||||
|
timesheet = make_timesheet(emp, simulate=True, billable=1)
|
||||||
|
|
||||||
self.assertEqual(timesheet.total_hours, 2)
|
self.assertEqual(timesheet.total_hours, 2)
|
||||||
self.assertEqual(timesheet.total_billable_hours, 2)
|
self.assertEqual(timesheet.total_billable_hours, 2)
|
||||||
@ -35,8 +37,10 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
self.assertEqual(timesheet.total_billable_amount, 100)
|
self.assertEqual(timesheet.total_billable_amount, 100)
|
||||||
|
|
||||||
def test_timesheet_billing_amount_not_billable(self):
|
def test_timesheet_billing_amount_not_billable(self):
|
||||||
make_salary_structure_for_timesheet("_T-Employee-00001")
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=0)
|
|
||||||
|
make_salary_structure_for_timesheet(emp)
|
||||||
|
timesheet = make_timesheet(emp, simulate=True, billable=0)
|
||||||
|
|
||||||
self.assertEqual(timesheet.total_hours, 2)
|
self.assertEqual(timesheet.total_hours, 2)
|
||||||
self.assertEqual(timesheet.total_billable_hours, 0)
|
self.assertEqual(timesheet.total_billable_hours, 0)
|
||||||
@ -45,8 +49,10 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
self.assertEqual(timesheet.total_billable_amount, 0)
|
self.assertEqual(timesheet.total_billable_amount, 0)
|
||||||
|
|
||||||
def test_salary_slip_from_timesheet(self):
|
def test_salary_slip_from_timesheet(self):
|
||||||
salary_structure = make_salary_structure_for_timesheet("_T-Employee-00001")
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
timesheet = make_timesheet("_T-Employee-00001", simulate = True, billable=1)
|
|
||||||
|
salary_structure = make_salary_structure_for_timesheet(emp)
|
||||||
|
timesheet = make_timesheet(emp, simulate = True, billable=1)
|
||||||
salary_slip = make_salary_slip(timesheet.name)
|
salary_slip = make_salary_slip(timesheet.name)
|
||||||
salary_slip.submit()
|
salary_slip.submit()
|
||||||
|
|
||||||
@ -65,7 +71,9 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
self.assertEqual(timesheet.status, 'Submitted')
|
self.assertEqual(timesheet.status, 'Submitted')
|
||||||
|
|
||||||
def test_sales_invoice_from_timesheet(self):
|
def test_sales_invoice_from_timesheet(self):
|
||||||
timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1)
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
|
|
||||||
|
timesheet = make_timesheet(emp, simulate=True, billable=1)
|
||||||
sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
|
sales_invoice = make_sales_invoice(timesheet.name, '_Test Item', '_Test Customer')
|
||||||
sales_invoice.due_date = nowdate()
|
sales_invoice.due_date = nowdate()
|
||||||
sales_invoice.submit()
|
sales_invoice.submit()
|
||||||
@ -80,7 +88,9 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
self.assertEqual(item.rate, 50.00)
|
self.assertEqual(item.rate, 50.00)
|
||||||
|
|
||||||
def test_timesheet_billing_based_on_project(self):
|
def test_timesheet_billing_based_on_project(self):
|
||||||
timesheet = make_timesheet("_T-Employee-00001", simulate=True, billable=1, project = '_Test Project', company='_Test Company')
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
|
|
||||||
|
timesheet = make_timesheet(emp, simulate=True, billable=1, project = '_Test Project', company='_Test Company')
|
||||||
sales_invoice = create_sales_invoice(do_not_save=True)
|
sales_invoice = create_sales_invoice(do_not_save=True)
|
||||||
sales_invoice.project = '_Test Project'
|
sales_invoice.project = '_Test Project'
|
||||||
sales_invoice.submit()
|
sales_invoice.submit()
|
||||||
@ -90,6 +100,8 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
self.assertEqual(ts.time_logs[0].sales_invoice, sales_invoice.name)
|
self.assertEqual(ts.time_logs[0].sales_invoice, sales_invoice.name)
|
||||||
|
|
||||||
def test_timesheet_time_overlap(self):
|
def test_timesheet_time_overlap(self):
|
||||||
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
|
|
||||||
settings = frappe.get_single('Projects Settings')
|
settings = frappe.get_single('Projects Settings')
|
||||||
initial_setting = settings.ignore_employee_time_overlap
|
initial_setting = settings.ignore_employee_time_overlap
|
||||||
settings.ignore_employee_time_overlap = 0
|
settings.ignore_employee_time_overlap = 0
|
||||||
@ -97,7 +109,7 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
|
|
||||||
update_activity_type("_Test Activity Type")
|
update_activity_type("_Test Activity Type")
|
||||||
timesheet = frappe.new_doc("Timesheet")
|
timesheet = frappe.new_doc("Timesheet")
|
||||||
timesheet.employee = "_T-Employee-00001"
|
timesheet.employee = emp
|
||||||
timesheet.append(
|
timesheet.append(
|
||||||
'time_logs',
|
'time_logs',
|
||||||
{
|
{
|
||||||
@ -129,12 +141,14 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
def test_timesheet_std_working_hours(self):
|
def test_timesheet_std_working_hours(self):
|
||||||
|
emp = make_employee("test_employee_6@salary.com")
|
||||||
|
|
||||||
company = frappe.get_doc('Company', "_Test Company")
|
company = frappe.get_doc('Company', "_Test Company")
|
||||||
company.standard_working_hours = 8
|
company.standard_working_hours = 8
|
||||||
company.save()
|
company.save()
|
||||||
|
|
||||||
timesheet = frappe.new_doc("Timesheet")
|
timesheet = frappe.new_doc("Timesheet")
|
||||||
timesheet.employee = "_T-Employee-00001"
|
timesheet.employee = emp
|
||||||
timesheet.company = '_Test Company'
|
timesheet.company = '_Test Company'
|
||||||
timesheet.append(
|
timesheet.append(
|
||||||
'time_logs',
|
'time_logs',
|
||||||
@ -156,7 +170,7 @@ class TestTimesheet(unittest.TestCase):
|
|||||||
company.save()
|
company.save()
|
||||||
|
|
||||||
timesheet = frappe.new_doc("Timesheet")
|
timesheet = frappe.new_doc("Timesheet")
|
||||||
timesheet.employee = "_T-Employee-00001"
|
timesheet.employee = emp
|
||||||
timesheet.company = '_Test Company'
|
timesheet.company = '_Test Company'
|
||||||
timesheet.append(
|
timesheet.append(
|
||||||
'time_logs',
|
'time_logs',
|
||||||
|
@ -73,6 +73,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
me.frm.set_query('contact_person', erpnext.queries.contact_query);
|
me.frm.set_query('contact_person', erpnext.queries.contact_query);
|
||||||
me.frm.set_query('supplier_address', erpnext.queries.address_query);
|
me.frm.set_query('supplier_address', erpnext.queries.address_query);
|
||||||
|
|
||||||
|
me.frm.set_query('billing_address', erpnext.queries.company_address_query);
|
||||||
|
|
||||||
if(this.frm.fields_dict.supplier) {
|
if(this.frm.fields_dict.supplier) {
|
||||||
this.frm.set_query("supplier", function() {
|
this.frm.set_query("supplier", function() {
|
||||||
return{ query: "erpnext.controllers.queries.supplier_query" }});
|
return{ query: "erpnext.controllers.queries.supplier_query" }});
|
||||||
@ -283,6 +285,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
|||||||
"shipping_address_display", true);
|
"shipping_address_display", true);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
billing_address: function() {
|
||||||
|
erpnext.utils.get_address_display(this.frm, "billing_address",
|
||||||
|
"billing_address_display", true);
|
||||||
|
},
|
||||||
|
|
||||||
tc_name: function() {
|
tc_name: function() {
|
||||||
this.get_terms();
|
this.get_terms();
|
||||||
},
|
},
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>
|
{{ address_line1 }}<br>{% if address_line2 %}{{ address_line2 }}<br>{% endif -%}{{ city }}<br>
|
||||||
{% if gst_state %}{{ gst_state }}{% endif -%}
|
{% if gst_state %}{{ gst_state }}{% endif -%}
|
||||||
{% if gst_state_number %}, State Code: {{ gst_state_number }}<br>{% endif -%}
|
{% if gst_state_number %}, State Code: {{ gst_state_number }}<br>{% endif -%}
|
||||||
{% if pincode %}PIN: {{ pincode }}<br>{% endif -%}
|
{% if pincode %}Postal Code: {{ pincode }}<br>{% endif -%}
|
||||||
{{ country }}<br>
|
{{ country }}<br>
|
||||||
{% if phone %}Phone: {{ phone }}<br>{% endif -%}
|
{% if phone %}Phone: {{ phone }}<br>{% endif -%}
|
||||||
{% if fax %}Fax: {{ fax }}<br>{% endif -%}
|
{% if fax %}Fax: {{ fax }}<br>{% endif -%}
|
||||||
|
@ -9,14 +9,14 @@ def setup(company=None, patch=True):
|
|||||||
make_custom_fields()
|
make_custom_fields()
|
||||||
add_print_formats()
|
add_print_formats()
|
||||||
|
|
||||||
def make_custom_fields():
|
def make_custom_fields(update=True):
|
||||||
custom_fields = {
|
custom_fields = {
|
||||||
'Supplier': [
|
'Supplier': [
|
||||||
dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id',
|
dict(fieldname='irs_1099', fieldtype='Check', insert_after='tax_id',
|
||||||
label='Is IRS 1099 reporting required for supplier?')
|
label='Is IRS 1099 reporting required for supplier?')
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
create_custom_fields(custom_fields)
|
create_custom_fields(custom_fields, update=update)
|
||||||
|
|
||||||
def add_print_formats():
|
def add_print_formats():
|
||||||
frappe.reload_doc("regional", "print_format", "irs_1099_form")
|
frappe.reload_doc("regional", "print_format", "irs_1099_form")
|
||||||
|
@ -3,16 +3,19 @@
|
|||||||
|
|
||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
import frappe
|
import frappe
|
||||||
|
import json
|
||||||
from frappe.model.naming import set_name_by_naming_series
|
from frappe.model.naming import set_name_by_naming_series
|
||||||
from frappe import _, msgprint, throw
|
from frappe import _, msgprint
|
||||||
import frappe.defaults
|
import frappe.defaults
|
||||||
from frappe.utils import flt, cint, cstr, today
|
from frappe.utils import flt, cint, cstr, today, get_formatted_email
|
||||||
from frappe.desk.reportview import build_match_conditions, get_filters_cond
|
from frappe.desk.reportview import build_match_conditions, get_filters_cond
|
||||||
from erpnext.utilities.transaction_base import TransactionBase
|
from erpnext.utilities.transaction_base import TransactionBase
|
||||||
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
|
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
|
||||||
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
|
from frappe.contacts.address_and_contact import load_address_and_contact, delete_contact_and_address
|
||||||
from frappe.model.rename_doc import update_linked_doctypes
|
from frappe.model.rename_doc import update_linked_doctypes
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
from frappe.utils.user import get_users_with_role
|
||||||
|
|
||||||
|
|
||||||
class Customer(TransactionBase):
|
class Customer(TransactionBase):
|
||||||
def get_feed(self):
|
def get_feed(self):
|
||||||
@ -378,10 +381,45 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False,
|
|||||||
.format(customer, customer_outstanding, credit_limit))
|
.format(customer, customer_outstanding, credit_limit))
|
||||||
|
|
||||||
# If not authorized person raise exception
|
# If not authorized person raise exception
|
||||||
credit_controller = frappe.db.get_value('Accounts Settings', None, 'credit_controller')
|
credit_controller_role = frappe.db.get_single_value('Accounts Settings', 'credit_controller')
|
||||||
if not credit_controller or credit_controller not in frappe.get_roles():
|
if not credit_controller_role or credit_controller_role not in frappe.get_roles():
|
||||||
throw(_("Please contact to the user who have Sales Master Manager {0} role")
|
# form a list of emails for the credit controller users
|
||||||
.format(" / " + credit_controller if credit_controller else ""))
|
credit_controller_users = get_users_with_role(credit_controller_role or "Sales Master Manager")
|
||||||
|
|
||||||
|
# form a list of emails and names to show to the user
|
||||||
|
credit_controller_users_list = [user for user in credit_controller_users if frappe.db.exists("Employee", {"prefered_email": user})]
|
||||||
|
credit_controller_users = [get_formatted_email(user).replace("<", "(").replace(">", ")") for user in credit_controller_users_list]
|
||||||
|
|
||||||
|
if not credit_controller_users:
|
||||||
|
frappe.throw(_("Please contact your administrator to extend the credit limits for {0}.".format(customer)))
|
||||||
|
|
||||||
|
message = """Please contact any of the following users to extend the credit limits for {0}:
|
||||||
|
<br><br><ul><li>{1}</li></ul>""".format(customer, '<li>'.join(credit_controller_users))
|
||||||
|
|
||||||
|
# if the current user does not have permissions to override credit limit,
|
||||||
|
# prompt them to send out an email to the controller users
|
||||||
|
frappe.msgprint(message,
|
||||||
|
title="Notify",
|
||||||
|
raise_exception=1,
|
||||||
|
primary_action={
|
||||||
|
'label': 'Send Email',
|
||||||
|
'server_action': 'erpnext.selling.doctype.customer.customer.send_emails',
|
||||||
|
'args': {
|
||||||
|
'customer': customer,
|
||||||
|
'customer_outstanding': customer_outstanding,
|
||||||
|
'credit_limit': credit_limit,
|
||||||
|
'credit_controller_users_list': credit_controller_users_list
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def send_emails(args):
|
||||||
|
args = json.loads(args)
|
||||||
|
subject = (_("Credit limit reached for customer {0}").format(args.get('customer')))
|
||||||
|
message = (_("Credit limit has been crossed for customer {0} ({1}/{2})")
|
||||||
|
.format(args.get('customer'), args.get('customer_outstanding'), args.get('credit_limit')))
|
||||||
|
frappe.sendmail(recipients=[args.get('credit_controller_users_list')], subject=subject, message=message)
|
||||||
|
|
||||||
def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None):
|
def get_customer_outstanding(customer, company, ignore_outstanding_sales_order=False, cost_center=None):
|
||||||
# Outstanding based on GL Entries
|
# Outstanding based on GL Entries
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"allow_import": 1,
|
"allow_import": 1,
|
||||||
"allow_rename": 1,
|
"allow_rename": 1,
|
||||||
"autoname": "field:title",
|
"autoname": "field:title",
|
||||||
@ -49,7 +50,7 @@
|
|||||||
"fieldname": "terms_and_conditions_help",
|
"fieldname": "terms_and_conditions_help",
|
||||||
"fieldtype": "HTML",
|
"fieldtype": "HTML",
|
||||||
"label": "Terms and Conditions Help",
|
"label": "Terms and Conditions Help",
|
||||||
"options": "<h4>Standard Terms and Conditions Example</h4>\n\n<pre>Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n</pre>\n\n<h4>How to get fieldnames</h4>\n\n<p>The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)</p>\n\n<h4>Templating</h4>\n\n<p>Templates are compiled using the Jinja Templating Langauge. To learn more about Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">read this documentation.</a></p>"
|
"options": "<h4>Standard Terms and Conditions Example</h4>\n\n<pre>Delivery Terms for Order number {{ name }}\n\n-Order Date : {{ transaction_date }} \n-Expected Delivery Date : {{ delivery_date }}\n</pre>\n\n<h4>How to get fieldnames</h4>\n\n<p>The fieldnames you can use in your email template are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)</p>\n\n<h4>Templating</h4>\n\n<p>Templates are compiled using the Jinja Templating Language. To learn more about Jinja, <a class=\"strong\" href=\"http://jinja.pocoo.org/docs/dev/templates/\">read this documentation.</a></p>"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "applicable_modules_section",
|
"fieldname": "applicable_modules_section",
|
||||||
@ -81,7 +82,8 @@
|
|||||||
],
|
],
|
||||||
"icon": "icon-legal",
|
"icon": "icon-legal",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"modified": "2019-07-04 13:31:30.393425",
|
"links": [],
|
||||||
|
"modified": "2020-06-16 22:54:38.094844",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Terms and Conditions",
|
"name": "Terms and Conditions",
|
||||||
|
@ -105,3 +105,4 @@ def add_company_to_session_defaults():
|
|||||||
"ref_doctype": "Company"
|
"ref_doctype": "Company"
|
||||||
})
|
})
|
||||||
settings.save()
|
settings.save()
|
||||||
|
|
||||||
|
@ -42,9 +42,9 @@ def get_cart_quotation(doc=None):
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
"doc": decorate_quotation_doc(doc),
|
"doc": decorate_quotation_doc(doc),
|
||||||
"shipping_addresses": [{"name": address.name, "display": address.display}
|
"shipping_addresses": [{"name": address.name, "title": address.address_title, "display": address.display}
|
||||||
for address in addresses if address.address_type == "Shipping"],
|
for address in addresses if address.address_type == "Shipping"],
|
||||||
"billing_addresses": [{"name": address.name, "display": address.display}
|
"billing_addresses": [{"name": address.name, "title": address.address_title, "display": address.display}
|
||||||
for address in addresses if address.address_type == "Billing"],
|
for address in addresses if address.address_type == "Billing"],
|
||||||
"shipping_rules": get_applicable_shipping_rules(party),
|
"shipping_rules": get_applicable_shipping_rules(party),
|
||||||
"cart_settings": frappe.get_cached_doc("Shopping Cart Settings")
|
"cart_settings": frappe.get_cached_doc("Shopping Cart Settings")
|
||||||
@ -78,8 +78,10 @@ def place_order():
|
|||||||
|
|
||||||
if is_stock_item:
|
if is_stock_item:
|
||||||
item_stock = get_qty_in_stock(item.item_code, "website_warehouse")
|
item_stock = get_qty_in_stock(item.item_code, "website_warehouse")
|
||||||
|
if not cint(item_stock.in_stock):
|
||||||
|
throw(_("{1} Not in Stock").format(item.item_code))
|
||||||
if item.qty > item_stock.stock_qty[0][0]:
|
if item.qty > item_stock.stock_qty[0][0]:
|
||||||
throw(_("Only {0} in stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code))
|
throw(_("Only {0} in Stock for item {1}").format(item_stock.stock_qty[0][0], item.item_code))
|
||||||
|
|
||||||
sales_order.flags.ignore_permissions = True
|
sales_order.flags.ignore_permissions = True
|
||||||
sales_order.insert()
|
sales_order.insert()
|
||||||
|
@ -5,31 +5,26 @@ import frappe
|
|||||||
import json
|
import json
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import nowdate
|
from frappe.utils import nowdate
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.dashboard_fixtures import _get_fiscal_year
|
||||||
|
from erpnext.buying.dashboard_fixtures import get_company_for_dashboards
|
||||||
|
|
||||||
def get_data():
|
def get_data():
|
||||||
|
fiscal_year = _get_fiscal_year(nowdate())
|
||||||
|
|
||||||
|
if not fiscal_year:
|
||||||
|
return frappe._dict()
|
||||||
|
|
||||||
|
company = frappe.get_doc("Company", get_company_for_dashboards())
|
||||||
|
fiscal_year_name = fiscal_year.get("name")
|
||||||
|
start_date = str(fiscal_year.get("year_start_date"))
|
||||||
|
end_date = str(fiscal_year.get("year_end_date"))
|
||||||
|
|
||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
"dashboards": get_dashboards(),
|
"dashboards": get_dashboards(),
|
||||||
"charts": get_charts(),
|
"charts": get_charts(company, fiscal_year_name, start_date, end_date),
|
||||||
"number_cards": get_number_cards(),
|
"number_cards": get_number_cards(company, fiscal_year_name, start_date, end_date),
|
||||||
})
|
})
|
||||||
|
|
||||||
def get_company_for_dashboards():
|
|
||||||
company = frappe.defaults.get_defaults().company
|
|
||||||
if company:
|
|
||||||
return company
|
|
||||||
else:
|
|
||||||
company_list = frappe.get_list("Company")
|
|
||||||
if company_list:
|
|
||||||
return company_list[0].name
|
|
||||||
return None
|
|
||||||
|
|
||||||
company = frappe.get_doc("Company", get_company_for_dashboards())
|
|
||||||
fiscal_year = get_fiscal_year(nowdate(), as_dict=1)
|
|
||||||
fiscal_year_name = fiscal_year.get("name")
|
|
||||||
start_date = str(fiscal_year.get("year_start_date"))
|
|
||||||
end_date = str(fiscal_year.get("year_end_date"))
|
|
||||||
|
|
||||||
def get_dashboards():
|
def get_dashboards():
|
||||||
return [{
|
return [{
|
||||||
"name": "Stock",
|
"name": "Stock",
|
||||||
@ -48,7 +43,7 @@ def get_dashboards():
|
|||||||
]
|
]
|
||||||
}]
|
}]
|
||||||
|
|
||||||
def get_charts():
|
def get_charts(company, fiscal_year_name, start_date, end_date):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"doctype": "Dashboard Chart",
|
"doctype": "Dashboard Chart",
|
||||||
@ -133,7 +128,7 @@ def get_charts():
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
def get_number_cards():
|
def get_number_cards(company, fiscal_year_name, start_date, end_date):
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
"name": "Total Active Items",
|
"name": "Total Active Items",
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -180,10 +180,10 @@ def get_fifo_queue(filters, sle=None):
|
|||||||
qty_to_pop = abs(d.actual_qty)
|
qty_to_pop = abs(d.actual_qty)
|
||||||
while qty_to_pop:
|
while qty_to_pop:
|
||||||
batch = fifo_queue[0] if fifo_queue else [0, None]
|
batch = fifo_queue[0] if fifo_queue else [0, None]
|
||||||
if 0 < batch[0] <= qty_to_pop:
|
if 0 < flt(batch[0]) <= qty_to_pop:
|
||||||
# if batch qty > 0
|
# if batch qty > 0
|
||||||
# not enough or exactly same qty in current batch, clear batch
|
# not enough or exactly same qty in current batch, clear batch
|
||||||
qty_to_pop -= batch[0]
|
qty_to_pop -= flt(batch[0])
|
||||||
transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0))
|
transferred_item_details[(d.voucher_no, d.name)].append(fifo_queue.pop(0))
|
||||||
else:
|
else:
|
||||||
# all from current batch
|
# all from current batch
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Service Level Agreement",
|
"label": "Service Level Agreement",
|
||||||
"links": "[\n {\n \"description\": \"Service Level.\",\n \"label\": \"Service Level\",\n \"name\": \"Service Level\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]"
|
"links": "[\n {\n \"description\": \"Service Level Agreement.\",\n \"label\": \"Service Level Agreement\",\n \"name\": \"Service Level Agreement\",\n \"type\": \"doctype\"\n }\n]"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
@ -43,7 +43,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Support",
|
"label": "Support",
|
||||||
"modified": "2020-05-28 13:51:23.869954",
|
"modified": "2020-06-04 11:54:56.124219",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Support",
|
"name": "Support",
|
||||||
@ -65,8 +65,8 @@
|
|||||||
"type": "DocType"
|
"type": "DocType"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"label": "Service Level",
|
"label": "Service Level Agreement",
|
||||||
"link_to": "Service Level",
|
"link_to": "Service Level Agreement",
|
||||||
"type": "DocType"
|
"type": "DocType"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -38,10 +38,35 @@ frappe.ui.form.on("Issue", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
refresh: function (frm) {
|
refresh: function (frm) {
|
||||||
|
|
||||||
if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") {
|
if (frm.doc.status !== "Closed" && frm.doc.agreement_fulfilled === "Ongoing") {
|
||||||
if (frm.doc.service_level_agreement) {
|
if (frm.doc.service_level_agreement) {
|
||||||
set_time_to_resolve_and_response(frm);
|
frappe.call({
|
||||||
|
'method': 'frappe.client.get',
|
||||||
|
args: {
|
||||||
|
doctype: 'Service Level Agreement',
|
||||||
|
name: frm.doc.service_level_agreement
|
||||||
|
},
|
||||||
|
callback: function(data) {
|
||||||
|
let statuses = data.message.pause_sla_on;
|
||||||
|
const hold_statuses = [];
|
||||||
|
$.each(statuses, (_i, entry) => {
|
||||||
|
hold_statuses.push(entry.status);
|
||||||
|
});
|
||||||
|
if (hold_statuses.includes(frm.doc.status)) {
|
||||||
|
frm.dashboard.clear_headline();
|
||||||
|
let message = {"indicator": "orange", "msg": __("SLA is on hold since {0}", [moment(frm.doc.on_hold_since).fromNow(true)])};
|
||||||
|
frm.dashboard.set_headline_alert(
|
||||||
|
'<div class="row">' +
|
||||||
|
'<div class="col-xs-12">' +
|
||||||
|
'<span class="indicator whitespace-nowrap '+ message.indicator +'"><span>'+ message.msg +'</span></span> ' +
|
||||||
|
'</div>' +
|
||||||
|
'</div>'
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
set_time_to_resolve_and_response(frm);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
frm.add_custom_button(__("Close"), function () {
|
frm.add_custom_button(__("Close"), function () {
|
||||||
@ -55,6 +80,7 @@ frappe.ui.form.on("Issue", {
|
|||||||
frm: frm
|
frm: frm
|
||||||
});
|
});
|
||||||
}, __("Make"));
|
}, __("Make"));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (frm.doc.service_level_agreement) {
|
if (frm.doc.service_level_agreement) {
|
||||||
frm.dashboard.clear_headline();
|
frm.dashboard.clear_headline();
|
||||||
|
@ -31,9 +31,13 @@
|
|||||||
"resolution_by",
|
"resolution_by",
|
||||||
"resolution_by_variance",
|
"resolution_by_variance",
|
||||||
"service_level_agreement_creation",
|
"service_level_agreement_creation",
|
||||||
|
"on_hold_since",
|
||||||
|
"total_hold_time",
|
||||||
"response",
|
"response",
|
||||||
"mins_to_first_response",
|
"mins_to_first_response",
|
||||||
"first_responded_on",
|
"first_responded_on",
|
||||||
|
"column_break_26",
|
||||||
|
"avg_response_time",
|
||||||
"additional_info",
|
"additional_info",
|
||||||
"lead",
|
"lead",
|
||||||
"contact",
|
"contact",
|
||||||
@ -50,7 +54,9 @@
|
|||||||
"resolution_date",
|
"resolution_date",
|
||||||
"content_type",
|
"content_type",
|
||||||
"attachment",
|
"attachment",
|
||||||
"via_customer_portal"
|
"via_customer_portal",
|
||||||
|
"resolution_time",
|
||||||
|
"user_resolution_time"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -114,7 +120,7 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"oldfieldname": "status",
|
"oldfieldname": "status",
|
||||||
"oldfieldtype": "Select",
|
"oldfieldtype": "Select",
|
||||||
"options": "Open\nReplied\nHold\nClosed",
|
"options": "Open\nReplied\nHold\nResolved\nClosed",
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -161,6 +167,7 @@
|
|||||||
"options": "Service Level Agreement"
|
"options": "Service Level Agreement"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.status != 'Replied';",
|
||||||
"fieldname": "response_by",
|
"fieldname": "response_by",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Response By",
|
"label": "Response By",
|
||||||
@ -174,6 +181,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"depends_on": "eval: doc.status != 'Replied';",
|
||||||
"fieldname": "resolution_by",
|
"fieldname": "resolution_by",
|
||||||
"fieldtype": "Datetime",
|
"fieldtype": "Datetime",
|
||||||
"label": "Resolution By",
|
"label": "Resolution By",
|
||||||
@ -328,7 +336,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.service_level_agreement",
|
"depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';",
|
||||||
"description": "in hours",
|
"description": "in hours",
|
||||||
"fieldname": "response_by_variance",
|
"fieldname": "response_by_variance",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
@ -336,7 +344,7 @@
|
|||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"depends_on": "eval: doc.service_level_agreement",
|
"depends_on": "eval: doc.service_level_agreement && doc.status != 'Replied';",
|
||||||
"description": "in hours",
|
"description": "in hours",
|
||||||
"fieldname": "resolution_by_variance",
|
"fieldname": "resolution_by_variance",
|
||||||
"fieldtype": "Float",
|
"fieldtype": "Float",
|
||||||
@ -362,12 +370,48 @@
|
|||||||
"label": "Issue Split From",
|
"label": "Issue Split From",
|
||||||
"options": "Issue",
|
"options": "Issue",
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_26",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"bold": 1,
|
||||||
|
"fieldname": "avg_response_time",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"label": "Average Response Time",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "resolution_time",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"label": "Resolution Time",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "user_resolution_time",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"label": "User Resolution Time",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "on_hold_since",
|
||||||
|
"fieldtype": "Datetime",
|
||||||
|
"hidden": 1,
|
||||||
|
"label": "On Hold Since",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "total_hold_time",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"label": "Total Hold Time",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "fa fa-ticket",
|
"icon": "fa fa-ticket",
|
||||||
"idx": 7,
|
"idx": 7,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2020-03-13 02:19:49.477928",
|
"modified": "2020-06-10 12:47:37.146914",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Issue",
|
"name": "Issue",
|
||||||
|
@ -7,7 +7,7 @@ import json
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe import utils
|
from frappe import utils
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import now, time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime
|
from frappe.utils import time_diff_in_hours, now_datetime, getdate, get_weekdays, add_to_date, today, get_time, get_datetime, time_diff_in_seconds, time_diff
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.utils.user import is_website_user
|
from frappe.utils.user import is_website_user
|
||||||
@ -47,8 +47,8 @@ class Issue(Document):
|
|||||||
self.contact = frappe.db.get_value("Contact", {"email_id": email_id})
|
self.contact = frappe.db.get_value("Contact", {"email_id": email_id})
|
||||||
|
|
||||||
if self.contact:
|
if self.contact:
|
||||||
contact = frappe.get_doc('Contact', self.contact)
|
contact = frappe.get_doc("Contact", self.contact)
|
||||||
self.customer = contact.get_link_for('Customer')
|
self.customer = contact.get_link_for("Customer")
|
||||||
|
|
||||||
if not self.company:
|
if not self.company:
|
||||||
self.company = frappe.db.get_value("Lead", self.lead, "company") or \
|
self.company = frappe.db.get_value("Lead", self.lead, "company") or \
|
||||||
@ -56,18 +56,70 @@ class Issue(Document):
|
|||||||
|
|
||||||
def update_status(self):
|
def update_status(self):
|
||||||
status = frappe.db.get_value("Issue", self.name, "status")
|
status = frappe.db.get_value("Issue", self.name, "status")
|
||||||
if self.status!="Open" and status =="Open" and not self.first_responded_on:
|
if self.status != "Open" and status == "Open" and not self.first_responded_on:
|
||||||
self.first_responded_on = frappe.flags.current_time or now_datetime()
|
self.first_responded_on = frappe.flags.current_time or now_datetime()
|
||||||
|
|
||||||
if self.status=="Closed" and status !="Closed":
|
if self.status in ["Closed", "Resolved"] and status not in ["Resolved", "Closed"]:
|
||||||
self.resolution_date = frappe.flags.current_time or now_datetime()
|
self.resolution_date = frappe.flags.current_time or now_datetime()
|
||||||
if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing":
|
if frappe.db.get_value("Issue", self.name, "agreement_fulfilled") == "Ongoing":
|
||||||
set_service_level_agreement_variance(issue=self.name)
|
set_service_level_agreement_variance(issue=self.name)
|
||||||
self.update_agreement_status()
|
self.update_agreement_status()
|
||||||
|
set_resolution_time(issue=self)
|
||||||
|
set_user_resolution_time(issue=self)
|
||||||
|
|
||||||
if self.status=="Open" and status !="Open":
|
if self.status == "Open" and status != "Open":
|
||||||
# if no date, it should be set as None and not a blank string "", as per mysql strict config
|
# if no date, it should be set as None and not a blank string "", as per mysql strict config
|
||||||
self.resolution_date = None
|
self.resolution_date = None
|
||||||
|
self.reset_issue_metrics()
|
||||||
|
# enable SLA and variance on Reopen
|
||||||
|
self.agreement_fulfilled = "Ongoing"
|
||||||
|
set_service_level_agreement_variance(issue=self.name)
|
||||||
|
|
||||||
|
self.handle_hold_time(status)
|
||||||
|
|
||||||
|
def handle_hold_time(self, status):
|
||||||
|
if self.service_level_agreement:
|
||||||
|
# set response and resolution variance as None as the issue is on Hold for status as Replied
|
||||||
|
pause_sla_on = frappe.db.get_all("Pause SLA On Status", fields=["status"],
|
||||||
|
filters={"parent": self.service_level_agreement})
|
||||||
|
hold_statuses = [entry.status for entry in pause_sla_on]
|
||||||
|
update_values = {}
|
||||||
|
|
||||||
|
if self.status in hold_statuses and status not in hold_statuses:
|
||||||
|
update_values['on_hold_since'] = frappe.flags.current_time or now_datetime()
|
||||||
|
if not self.first_responded_on:
|
||||||
|
update_values['response_by'] = None
|
||||||
|
update_values['response_by_variance'] = 0
|
||||||
|
update_values['resolution_by'] = None
|
||||||
|
update_values['resolution_by_variance'] = 0
|
||||||
|
|
||||||
|
# calculate hold time when status is changed from Replied to any other status
|
||||||
|
if self.status not in hold_statuses and status in hold_statuses:
|
||||||
|
hold_time = self.total_hold_time if self.total_hold_time else 0
|
||||||
|
now_time = frappe.flags.current_time or now_datetime()
|
||||||
|
update_values['total_hold_time'] = hold_time + time_diff_in_seconds(now_time, self.on_hold_since)
|
||||||
|
|
||||||
|
# re-calculate SLA variables after issue changes from Replied to Open
|
||||||
|
# add hold time to SLA variables
|
||||||
|
if self.status == "Open" and status in hold_statuses:
|
||||||
|
start_date_time = get_datetime(self.service_level_agreement_creation)
|
||||||
|
priority = get_priority(self)
|
||||||
|
now_time = frappe.flags.current_time or now_datetime()
|
||||||
|
hold_time = time_diff_in_seconds(now_time, self.on_hold_since)
|
||||||
|
|
||||||
|
if not self.first_responded_on:
|
||||||
|
response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
|
||||||
|
update_values['response_by'] = add_to_date(response_by, seconds=round(hold_time))
|
||||||
|
response_by_variance = round(time_diff_in_hours(self.response_by, now_time))
|
||||||
|
update_values['response_by_variance'] = response_by_variance + (hold_time // 3600)
|
||||||
|
|
||||||
|
resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
|
||||||
|
update_values['resolution_by'] = add_to_date(resolution_by, seconds=round(hold_time))
|
||||||
|
resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_time))
|
||||||
|
update_values['resolution_by_variance'] = resolution_by_variance + (hold_time // 3600)
|
||||||
|
update_values['on_hold_since'] = None
|
||||||
|
|
||||||
|
self.db_set(update_values)
|
||||||
|
|
||||||
def update_agreement_status(self):
|
def update_agreement_status(self):
|
||||||
if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
|
if self.service_level_agreement and self.agreement_fulfilled == "Ongoing":
|
||||||
@ -128,6 +180,7 @@ class Issue(Document):
|
|||||||
replicated_issue.response_by_variance = None
|
replicated_issue.response_by_variance = None
|
||||||
replicated_issue.resolution_by = None
|
replicated_issue.resolution_by = None
|
||||||
replicated_issue.resolution_by_variance = None
|
replicated_issue.resolution_by_variance = None
|
||||||
|
replicated_issue.reset_issue_metrics()
|
||||||
|
|
||||||
frappe.get_doc(replicated_issue).insert()
|
frappe.get_doc(replicated_issue).insert()
|
||||||
|
|
||||||
@ -137,7 +190,7 @@ class Issue(Document):
|
|||||||
communications = frappe.get_all("Communication",
|
communications = frappe.get_all("Communication",
|
||||||
filters={"reference_doctype": "Issue",
|
filters={"reference_doctype": "Issue",
|
||||||
"reference_name": comm_to_split_from.reference_name,
|
"reference_name": comm_to_split_from.reference_name,
|
||||||
"creation": ('>=', comm_to_split_from.creation)})
|
"creation": (">=", comm_to_split_from.creation)})
|
||||||
|
|
||||||
for communication in communications:
|
for communication in communications:
|
||||||
doc = frappe.get_doc("Communication", communication.name)
|
doc = frappe.get_doc("Communication", communication.name)
|
||||||
@ -173,20 +226,15 @@ class Issue(Document):
|
|||||||
self.service_level_agreement = service_level_agreement.name
|
self.service_level_agreement = service_level_agreement.name
|
||||||
self.priority = service_level_agreement.default_priority if not priority else priority
|
self.priority = service_level_agreement.default_priority if not priority else priority
|
||||||
|
|
||||||
service_level_agreement = frappe.get_doc("Service Level Agreement", service_level_agreement.name)
|
priority = get_priority(self)
|
||||||
priority = service_level_agreement.get_service_level_agreement_priority(self.priority)
|
|
||||||
priority.update({
|
|
||||||
"support_and_resolution": service_level_agreement.support_and_resolution,
|
|
||||||
"holiday_list": service_level_agreement.holiday_list
|
|
||||||
})
|
|
||||||
|
|
||||||
if not self.creation:
|
if not self.creation:
|
||||||
self.creation = now_datetime()
|
self.creation = now_datetime()
|
||||||
self.service_level_agreement_creation = now_datetime()
|
self.service_level_agreement_creation = now_datetime()
|
||||||
|
|
||||||
start_date_time = get_datetime(self.service_level_agreement_creation)
|
start_date_time = get_datetime(self.service_level_agreement_creation)
|
||||||
self.response_by = get_expected_time_for(parameter='response', service_level=priority, start_date_time=start_date_time)
|
self.response_by = get_expected_time_for(parameter="response", service_level=priority, start_date_time=start_date_time)
|
||||||
self.resolution_by = get_expected_time_for(parameter='resolution', service_level=priority, start_date_time=start_date_time)
|
self.resolution_by = get_expected_time_for(parameter="resolution", service_level=priority, start_date_time=start_date_time)
|
||||||
|
|
||||||
self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime()))
|
self.response_by_variance = round(time_diff_in_hours(self.response_by, now_datetime()))
|
||||||
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()))
|
self.resolution_by_variance = round(time_diff_in_hours(self.resolution_by, now_datetime()))
|
||||||
@ -221,36 +269,41 @@ class Issue(Document):
|
|||||||
self.agreement_fulfilled = "Ongoing"
|
self.agreement_fulfilled = "Ongoing"
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
def reset_issue_metrics(self):
|
||||||
|
self.db_set("resolution_time", None)
|
||||||
|
self.db_set("user_resolution_time", None)
|
||||||
|
|
||||||
|
|
||||||
|
def get_priority(issue):
|
||||||
|
service_level_agreement = frappe.get_doc("Service Level Agreement", issue.service_level_agreement)
|
||||||
|
priority = service_level_agreement.get_service_level_agreement_priority(issue.priority)
|
||||||
|
priority.update({
|
||||||
|
"support_and_resolution": service_level_agreement.support_and_resolution,
|
||||||
|
"holiday_list": service_level_agreement.holiday_list
|
||||||
|
})
|
||||||
|
return priority
|
||||||
|
|
||||||
|
|
||||||
def get_expected_time_for(parameter, service_level, start_date_time):
|
def get_expected_time_for(parameter, service_level, start_date_time):
|
||||||
current_date_time = start_date_time
|
current_date_time = start_date_time
|
||||||
expected_time = current_date_time
|
expected_time = current_date_time
|
||||||
start_time = None
|
start_time = None
|
||||||
end_time = None
|
end_time = None
|
||||||
|
|
||||||
# lets assume response time is in days by default
|
if parameter == "response":
|
||||||
if parameter == 'response':
|
allotted_seconds = service_level.get("response_time")
|
||||||
allotted_days = service_level.get("response_time")
|
elif parameter == "resolution":
|
||||||
time_period = service_level.get("response_time_period")
|
allotted_seconds = service_level.get("resolution_time")
|
||||||
elif parameter == 'resolution':
|
|
||||||
allotted_days = service_level.get("resolution_time")
|
|
||||||
time_period = service_level.get("resolution_time_period")
|
|
||||||
else:
|
else:
|
||||||
frappe.throw(_("{0} parameter is invalid").format(parameter))
|
frappe.throw(_("{0} parameter is invalid").format(parameter))
|
||||||
|
|
||||||
allotted_hours = 0
|
expected_time_is_set = 0
|
||||||
if time_period == 'Hour':
|
|
||||||
allotted_hours = allotted_days
|
|
||||||
allotted_days = 0
|
|
||||||
elif time_period == 'Week':
|
|
||||||
allotted_days *= 7
|
|
||||||
|
|
||||||
expected_time_is_set = 1 if allotted_days == 0 and time_period in ['Day', 'Week'] else 0
|
|
||||||
|
|
||||||
support_days = {}
|
support_days = {}
|
||||||
for service in service_level.get("support_and_resolution"):
|
for service in service_level.get("support_and_resolution"):
|
||||||
support_days[service.workday] = frappe._dict({
|
support_days[service.workday] = frappe._dict({
|
||||||
'start_time': service.start_time,
|
"start_time": service.start_time,
|
||||||
'end_time': service.end_time,
|
"end_time": service.end_time,
|
||||||
})
|
})
|
||||||
|
|
||||||
holidays = get_holidays(service_level.get("holiday_list"))
|
holidays = get_holidays(service_level.get("holiday_list"))
|
||||||
@ -264,25 +317,22 @@ def get_expected_time_for(parameter, service_level, start_date_time):
|
|||||||
if getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time \
|
if getdate(current_date_time) == getdate(start_date_time) and get_time_in_timedelta(current_date_time.time()) > support_days[current_weekday].start_time \
|
||||||
else support_days[current_weekday].start_time
|
else support_days[current_weekday].start_time
|
||||||
end_time = support_days[current_weekday].end_time
|
end_time = support_days[current_weekday].end_time
|
||||||
time_left_today = time_diff_in_hours(end_time, start_time)
|
time_left_today = time_diff_in_seconds(end_time, start_time)
|
||||||
|
|
||||||
# no time left for support today
|
# no time left for support today
|
||||||
if time_left_today < 0: pass
|
if time_left_today <= 0: pass
|
||||||
elif time_period == 'Hour':
|
elif allotted_seconds:
|
||||||
if time_left_today >= allotted_hours:
|
if time_left_today >= allotted_seconds:
|
||||||
expected_time = datetime.combine(getdate(current_date_time), get_time(start_time))
|
expected_time = datetime.combine(getdate(current_date_time), get_time(start_time))
|
||||||
expected_time = add_to_date(expected_time, hours=allotted_hours)
|
expected_time = add_to_date(expected_time, seconds=allotted_seconds)
|
||||||
expected_time_is_set = 1
|
expected_time_is_set = 1
|
||||||
else:
|
else:
|
||||||
allotted_hours = allotted_hours - time_left_today
|
allotted_seconds = allotted_seconds - time_left_today
|
||||||
else:
|
|
||||||
allotted_days -= 1
|
|
||||||
expected_time_is_set = allotted_days <= 0
|
|
||||||
|
|
||||||
if not expected_time_is_set:
|
if not expected_time_is_set:
|
||||||
current_date_time = add_to_date(current_date_time, days=1)
|
current_date_time = add_to_date(current_date_time, days=1)
|
||||||
|
|
||||||
if end_time and time_period != 'Hour':
|
if end_time and allotted_seconds >= 86400:
|
||||||
current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time))
|
current_date_time = datetime.combine(getdate(current_date_time), get_time(end_time))
|
||||||
else:
|
else:
|
||||||
current_date_time = expected_time
|
current_date_time = expected_time
|
||||||
@ -311,6 +361,36 @@ def set_service_level_agreement_variance(issue=None):
|
|||||||
if variance < 0:
|
if variance < 0:
|
||||||
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
|
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_fulfilled", val="Failed", update_modified=False)
|
||||||
|
|
||||||
|
|
||||||
|
def set_resolution_time(issue):
|
||||||
|
# total time taken from issue creation to closing
|
||||||
|
resolution_time = time_diff_in_seconds(issue.resolution_date, issue.creation)
|
||||||
|
issue.db_set("resolution_time", resolution_time)
|
||||||
|
|
||||||
|
|
||||||
|
def set_user_resolution_time(issue):
|
||||||
|
# total time taken by a user to close the issue apart from wait_time
|
||||||
|
communications = frappe.get_list("Communication", filters={
|
||||||
|
"reference_doctype": issue.doctype,
|
||||||
|
"reference_name": issue.name
|
||||||
|
},
|
||||||
|
fields=["sent_or_received", "name", "creation"],
|
||||||
|
order_by="creation"
|
||||||
|
)
|
||||||
|
|
||||||
|
pending_time = []
|
||||||
|
for i in range(len(communications)):
|
||||||
|
if communications[i].sent_or_received == "Received" and communications[i-1].sent_or_received == "Sent":
|
||||||
|
wait_time = time_diff_in_seconds(communications[i].creation, communications[i-1].creation)
|
||||||
|
if wait_time > 0:
|
||||||
|
pending_time.append(wait_time)
|
||||||
|
|
||||||
|
total_pending_time = sum(pending_time)
|
||||||
|
resolution_time_in_secs = time_diff_in_seconds(issue.resolution_date, issue.creation)
|
||||||
|
user_resolution_time = resolution_time_in_secs - total_pending_time
|
||||||
|
issue.db_set("user_resolution_time", user_resolution_time)
|
||||||
|
|
||||||
|
|
||||||
def get_list_context(context=None):
|
def get_list_context(context=None):
|
||||||
return {
|
return {
|
||||||
"title": _("Issues"),
|
"title": _("Issues"),
|
||||||
@ -318,7 +398,7 @@ def get_list_context(context=None):
|
|||||||
"row_template": "templates/includes/issue_row.html",
|
"row_template": "templates/includes/issue_row.html",
|
||||||
"show_sidebar": True,
|
"show_sidebar": True,
|
||||||
"show_search": True,
|
"show_search": True,
|
||||||
'no_breadcrumbs': True
|
"no_breadcrumbs": True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -326,12 +406,12 @@ def get_issue_list(doctype, txt, filters, limit_start, limit_page_length=20, ord
|
|||||||
from frappe.www.list import get_list
|
from frappe.www.list import get_list
|
||||||
|
|
||||||
user = frappe.session.user
|
user = frappe.session.user
|
||||||
contact = frappe.db.get_value('Contact', {'user': user}, 'name')
|
contact = frappe.db.get_value("Contact", {"user": user}, "name")
|
||||||
customer = None
|
customer = None
|
||||||
|
|
||||||
if contact:
|
if contact:
|
||||||
contact_doc = frappe.get_doc('Contact', contact)
|
contact_doc = frappe.get_doc("Contact", contact)
|
||||||
customer = contact_doc.get_link_for('Customer')
|
customer = contact_doc.get_link_for("Customer")
|
||||||
|
|
||||||
ignore_permissions = False
|
ignore_permissions = False
|
||||||
if is_website_user():
|
if is_website_user():
|
||||||
|
@ -5,15 +5,18 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
|
from erpnext.support.doctype.service_level_agreement.test_service_level_agreement import create_service_level_agreements_for_issues
|
||||||
from frappe.utils import now_datetime, get_datetime
|
from frappe.utils import now_datetime, get_datetime, flt
|
||||||
import datetime
|
import datetime
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
|
||||||
class TestIssue(unittest.TestCase):
|
class TestIssue(unittest.TestCase):
|
||||||
def test_response_time_and_resolution_time_based_on_different_sla(self):
|
def setUp(self):
|
||||||
|
frappe.db.sql("delete from `tabService Level Agreement`")
|
||||||
|
frappe.db.sql("delete from `tabEmployee`")
|
||||||
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
|
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
|
||||||
create_service_level_agreements_for_issues()
|
create_service_level_agreements_for_issues()
|
||||||
|
|
||||||
|
def test_response_time_and_resolution_time_based_on_different_sla(self):
|
||||||
creation = datetime.datetime(2019, 3, 4, 12, 0)
|
creation = datetime.datetime(2019, 3, 4, 12, 0)
|
||||||
|
|
||||||
# make issue with customer specific SLA
|
# make issue with customer specific SLA
|
||||||
@ -72,8 +75,67 @@ class TestIssue(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(issue.agreement_fulfilled, 'Fulfilled')
|
self.assertEqual(issue.agreement_fulfilled, 'Fulfilled')
|
||||||
|
|
||||||
def make_issue(creation=None, customer=None, index=0):
|
def test_issue_metrics(self):
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 4, 0)
|
||||||
|
|
||||||
|
issue = make_issue(creation, index=1)
|
||||||
|
create_communication(issue.name, "test@example.com", "Received", creation)
|
||||||
|
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 4, 15)
|
||||||
|
create_communication(issue.name, "test@admin.com", "Sent", creation)
|
||||||
|
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 5, 0)
|
||||||
|
create_communication(issue.name, "test@example.com", "Received", creation)
|
||||||
|
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 5, 5)
|
||||||
|
create_communication(issue.name, "test@admin.com", "Sent", creation)
|
||||||
|
|
||||||
|
frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5)
|
||||||
|
issue.reload()
|
||||||
|
issue.status = 'Closed'
|
||||||
|
issue.save()
|
||||||
|
|
||||||
|
self.assertEqual(issue.avg_response_time, 600)
|
||||||
|
self.assertEqual(issue.resolution_time, 3900)
|
||||||
|
self.assertEqual(issue.user_resolution_time, 1200)
|
||||||
|
|
||||||
|
def test_hold_time_on_replied(self):
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 4, 0)
|
||||||
|
|
||||||
|
issue = make_issue(creation, index=1)
|
||||||
|
create_communication(issue.name, "test@example.com", "Received", creation)
|
||||||
|
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 4, 15)
|
||||||
|
create_communication(issue.name, "test@admin.com", "Sent", creation)
|
||||||
|
|
||||||
|
frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15)
|
||||||
|
issue.reload()
|
||||||
|
issue.status = 'Replied'
|
||||||
|
issue.save()
|
||||||
|
|
||||||
|
self.assertEqual(issue.on_hold_since, frappe.flags.current_time)
|
||||||
|
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 5, 0)
|
||||||
|
frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 0)
|
||||||
|
create_communication(issue.name, "test@example.com", "Received", creation)
|
||||||
|
|
||||||
|
issue.reload()
|
||||||
|
self.assertEqual(flt(issue.total_hold_time, 2), 2700)
|
||||||
|
self.assertEqual(issue.resolution_by, datetime.datetime(2020, 3, 4, 16, 45))
|
||||||
|
|
||||||
|
creation = datetime.datetime(2020, 3, 4, 5, 5)
|
||||||
|
create_communication(issue.name, "test@admin.com", "Sent", creation)
|
||||||
|
|
||||||
|
frappe.flags.current_time = datetime.datetime(2020, 3, 4, 5, 5)
|
||||||
|
issue.reload()
|
||||||
|
issue.status = 'Closed'
|
||||||
|
issue.save()
|
||||||
|
|
||||||
|
issue.reload()
|
||||||
|
self.assertEqual(flt(issue.total_hold_time, 2), 2700)
|
||||||
|
|
||||||
|
|
||||||
|
def make_issue(creation=None, customer=None, index=0):
|
||||||
issue = frappe.get_doc({
|
issue = frappe.get_doc({
|
||||||
"doctype": "Issue",
|
"doctype": "Issue",
|
||||||
"subject": "Service Level Agreement Issue {0}".format(index),
|
"subject": "Service Level Agreement Issue {0}".format(index),
|
||||||
@ -86,6 +148,7 @@ def make_issue(creation=None, customer=None, index=0):
|
|||||||
|
|
||||||
return issue
|
return issue
|
||||||
|
|
||||||
|
|
||||||
def create_customer(name, customer_group, territory):
|
def create_customer(name, customer_group, territory):
|
||||||
|
|
||||||
create_customer_group(customer_group)
|
create_customer_group(customer_group)
|
||||||
@ -99,6 +162,7 @@ def create_customer(name, customer_group, territory):
|
|||||||
"territory": territory
|
"territory": territory
|
||||||
}).insert(ignore_permissions=True)
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def create_customer_group(customer_group):
|
def create_customer_group(customer_group):
|
||||||
|
|
||||||
if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}):
|
if not frappe.db.exists("Customer Group", {"customer_group_name": customer_group}):
|
||||||
@ -107,6 +171,7 @@ def create_customer_group(customer_group):
|
|||||||
"customer_group_name": customer_group
|
"customer_group_name": customer_group
|
||||||
}).insert(ignore_permissions=True)
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
def create_territory(territory):
|
def create_territory(territory):
|
||||||
|
|
||||||
if not frappe.db.exists("Territory", {"territory_name": territory}):
|
if not frappe.db.exists("Territory", {"territory_name": territory}):
|
||||||
@ -114,3 +179,21 @@ def create_territory(territory):
|
|||||||
"doctype": "Territory",
|
"doctype": "Territory",
|
||||||
"territory_name": territory,
|
"territory_name": territory,
|
||||||
}).insert(ignore_permissions=True)
|
}).insert(ignore_permissions=True)
|
||||||
|
|
||||||
|
|
||||||
|
def create_communication(reference_name, sender, sent_or_received, creation):
|
||||||
|
issue = frappe.get_doc({
|
||||||
|
"doctype": "Communication",
|
||||||
|
"communication_type": "Communication",
|
||||||
|
"communication_medium": "Email",
|
||||||
|
"sent_or_received": sent_or_received,
|
||||||
|
"email_status": "Open",
|
||||||
|
"subject": "Test Issue",
|
||||||
|
"sender": sender,
|
||||||
|
"content": "Test",
|
||||||
|
"status": "Linked",
|
||||||
|
"reference_doctype": "Issue",
|
||||||
|
"creation": creation,
|
||||||
|
"reference_name": reference_name
|
||||||
|
})
|
||||||
|
issue.save()
|
||||||
|
@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"creation": "2020-06-05 13:59:43.265588",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"status"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "status",
|
||||||
|
"fieldtype": "Select",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Status",
|
||||||
|
"reqd": 1,
|
||||||
|
"show_days": 1,
|
||||||
|
"show_seconds": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2020-06-05 15:15:29.986608",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Support",
|
||||||
|
"name": "Pause SLA On Status",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"quick_entry": 1,
|
||||||
|
"sort_field": "modified",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"track_changes": 1
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
class PauseSLAOnStatus(Document):
|
||||||
|
pass
|
@ -1,6 +0,0 @@
|
|||||||
// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
// For license information, please see license.txt
|
|
||||||
|
|
||||||
frappe.ui.form.on('Service Level', {
|
|
||||||
|
|
||||||
});
|
|
@ -1,111 +0,0 @@
|
|||||||
{
|
|
||||||
"autoname": "field:service_level",
|
|
||||||
"creation": "2018-11-19 12:44:30.407502",
|
|
||||||
"doctype": "DocType",
|
|
||||||
"editable_grid": 1,
|
|
||||||
"engine": "InnoDB",
|
|
||||||
"field_order": [
|
|
||||||
"service_level",
|
|
||||||
"employee_group",
|
|
||||||
"column_break_2",
|
|
||||||
"holiday_list",
|
|
||||||
"default_priority",
|
|
||||||
"response_and_resoution_time",
|
|
||||||
"priorities",
|
|
||||||
"section_break_01",
|
|
||||||
"support_and_resolution"
|
|
||||||
],
|
|
||||||
"fields": [
|
|
||||||
{
|
|
||||||
"fieldname": "service_level",
|
|
||||||
"fieldtype": "Data",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Level",
|
|
||||||
"reqd": 1,
|
|
||||||
"unique": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "column_break_2",
|
|
||||||
"fieldtype": "Column Break"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "holiday_list",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Holiday List (ignored during SLA calculation)",
|
|
||||||
"options": "Holiday List",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "employee_group",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Employee Group",
|
|
||||||
"options": "Employee Group"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "response_and_resoution_time",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Response and Resoution Time"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "section_break_01",
|
|
||||||
"fieldtype": "Section Break",
|
|
||||||
"label": "Support Hours"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "support_and_resolution",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"label": "Support and Resolution",
|
|
||||||
"options": "Service Day",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "priorities",
|
|
||||||
"fieldtype": "Table",
|
|
||||||
"label": "Priorities",
|
|
||||||
"options": "Service Level Priority",
|
|
||||||
"reqd": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldname": "default_priority",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Default Priority",
|
|
||||||
"options": "Issue Priority",
|
|
||||||
"read_only": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"modified": "2019-06-06 12:58:03.464056",
|
|
||||||
"modified_by": "Administrator",
|
|
||||||
"module": "Support",
|
|
||||||
"name": "Service Level",
|
|
||||||
"owner": "Administrator",
|
|
||||||
"permissions": [
|
|
||||||
{
|
|
||||||
"create": 1,
|
|
||||||
"delete": 1,
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "System Manager",
|
|
||||||
"share": 1,
|
|
||||||
"write": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"create": 1,
|
|
||||||
"delete": 1,
|
|
||||||
"email": 1,
|
|
||||||
"export": 1,
|
|
||||||
"print": 1,
|
|
||||||
"read": 1,
|
|
||||||
"report": 1,
|
|
||||||
"role": "All",
|
|
||||||
"share": 1,
|
|
||||||
"write": 1
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"sort_field": "modified",
|
|
||||||
"sort_order": "DESC"
|
|
||||||
}
|
|
@ -1,95 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
|
||||||
# For license information, please see license.txt
|
|
||||||
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
import frappe
|
|
||||||
from frappe import _
|
|
||||||
from frappe.model.document import Document
|
|
||||||
from datetime import datetime
|
|
||||||
from frappe.utils import get_weekdays
|
|
||||||
|
|
||||||
class ServiceLevel(Document):
|
|
||||||
|
|
||||||
def validate(self):
|
|
||||||
self.check_priorities()
|
|
||||||
self.check_support_and_resolution()
|
|
||||||
|
|
||||||
def check_priorities(self):
|
|
||||||
default_priority = []
|
|
||||||
priorities = []
|
|
||||||
|
|
||||||
for priority in self.priorities:
|
|
||||||
# Check if response and resolution time is set for every priority
|
|
||||||
if not (priority.response_time or priority.resolution_time):
|
|
||||||
frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx))
|
|
||||||
|
|
||||||
priorities.append(priority.priority)
|
|
||||||
|
|
||||||
if priority.default_priority:
|
|
||||||
default_priority.append(priority.default_priority)
|
|
||||||
|
|
||||||
if priority.response_time_period == "Hour":
|
|
||||||
response = priority.response_time * 0.0416667
|
|
||||||
elif priority.response_time_period == "Day":
|
|
||||||
response = priority.response_time
|
|
||||||
elif priority.response_time_period == "Week":
|
|
||||||
response = priority.response_time * 7
|
|
||||||
|
|
||||||
if priority.resolution_time_period == "Hour":
|
|
||||||
resolution = priority.resolution_time * 0.0416667
|
|
||||||
elif priority.resolution_time_period == "Day":
|
|
||||||
resolution = priority.resolution_time
|
|
||||||
elif priority.resolution_time_period == "Week":
|
|
||||||
resolution = priority.resolution_time * 7
|
|
||||||
|
|
||||||
if response > resolution:
|
|
||||||
frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx))
|
|
||||||
|
|
||||||
# Check if repeated priority
|
|
||||||
if not len(set(priorities)) == len(priorities):
|
|
||||||
repeated_priority = get_repeated(priorities)
|
|
||||||
frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority))
|
|
||||||
|
|
||||||
# Check if repeated default priority
|
|
||||||
if not len(set(default_priority)) == len(default_priority):
|
|
||||||
frappe.throw(_("Select only one Priority as Default."))
|
|
||||||
|
|
||||||
# set default priority from priorities
|
|
||||||
try:
|
|
||||||
self.default_priority = next(d.priority for d in self.priorities if d.default_priority)
|
|
||||||
except Exception:
|
|
||||||
frappe.throw(_("Select a Default Priority."))
|
|
||||||
|
|
||||||
def check_support_and_resolution(self):
|
|
||||||
week = get_weekdays()
|
|
||||||
support_days = []
|
|
||||||
|
|
||||||
for support_and_resolution in self.support_and_resolution:
|
|
||||||
# Check if start and end time is set for every support day
|
|
||||||
if not (support_and_resolution.start_time or support_and_resolution.end_time):
|
|
||||||
frappe.throw(_("Set Start Time and End Time for \
|
|
||||||
Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx)))
|
|
||||||
|
|
||||||
support_days.append(support_and_resolution.workday)
|
|
||||||
support_and_resolution.idx = week.index(support_and_resolution.workday) + 1
|
|
||||||
|
|
||||||
if support_and_resolution.start_time >= support_and_resolution.end_time:
|
|
||||||
frappe.throw(_("Start Time can't be greater than or equal to End Time \
|
|
||||||
for {0}.".format(support_and_resolution.workday)))
|
|
||||||
|
|
||||||
# Check for repeated workday
|
|
||||||
if not len(set(support_days)) == len(support_days):
|
|
||||||
repeated_days = get_repeated(support_days)
|
|
||||||
frappe.throw(_("Workday {0} has been repeated.").format(repeated_days))
|
|
||||||
|
|
||||||
def get_repeated(values):
|
|
||||||
unique_list = []
|
|
||||||
diff = []
|
|
||||||
for value in values:
|
|
||||||
if value not in unique_list:
|
|
||||||
unique_list.append(str(value))
|
|
||||||
else:
|
|
||||||
if value not in diff:
|
|
||||||
diff.append(str(value))
|
|
||||||
return " ".join(diff)
|
|
@ -1,12 +0,0 @@
|
|||||||
from frappe import _
|
|
||||||
|
|
||||||
def get_data():
|
|
||||||
return {
|
|
||||||
'fieldname': 'service_level',
|
|
||||||
'transactions': [
|
|
||||||
{
|
|
||||||
'label': _('Service Level Agreement'),
|
|
||||||
'items': ['Service Level Agreement']
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,149 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
|
|
||||||
# See license.txt
|
|
||||||
from __future__ import unicode_literals
|
|
||||||
from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group
|
|
||||||
from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities
|
|
||||||
|
|
||||||
import frappe
|
|
||||||
import unittest
|
|
||||||
|
|
||||||
class TestServiceLevel(unittest.TestCase):
|
|
||||||
|
|
||||||
def test_service_level(self):
|
|
||||||
employee_group = make_employee_group()
|
|
||||||
make_holiday_list()
|
|
||||||
make_priorities()
|
|
||||||
|
|
||||||
# Default Service Level
|
|
||||||
test_make_service_level = create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6)
|
|
||||||
get_make_service_level = get_service_level("__Test Service Level")
|
|
||||||
|
|
||||||
self.assertEqual(test_make_service_level.name, get_make_service_level.name)
|
|
||||||
self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list)
|
|
||||||
self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group)
|
|
||||||
|
|
||||||
# Service Level
|
|
||||||
test_make_service_level = create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3)
|
|
||||||
get_make_service_level = get_service_level("_Test Service Level")
|
|
||||||
|
|
||||||
self.assertEqual(test_make_service_level.name, get_make_service_level.name)
|
|
||||||
self.assertEqual(test_make_service_level.holiday_list, get_make_service_level.holiday_list)
|
|
||||||
self.assertEqual(test_make_service_level.employee_group, get_make_service_level.employee_group)
|
|
||||||
|
|
||||||
|
|
||||||
def create_service_level(service_level, holiday_list, employee_group, response_time, resolution_time):
|
|
||||||
sl = frappe.get_doc({
|
|
||||||
"doctype": "Service Level",
|
|
||||||
"service_level": service_level,
|
|
||||||
"holiday_list": holiday_list,
|
|
||||||
"employee_group": employee_group,
|
|
||||||
"priorities": [
|
|
||||||
{
|
|
||||||
"priority": "Low",
|
|
||||||
"response_time": response_time,
|
|
||||||
"response_time_period": "Hour",
|
|
||||||
"resolution_time": resolution_time,
|
|
||||||
"resolution_time_period": "Hour",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"priority": "Medium",
|
|
||||||
"response_time": response_time,
|
|
||||||
"default_priority": 1,
|
|
||||||
"response_time_period": "Hour",
|
|
||||||
"resolution_time": resolution_time,
|
|
||||||
"resolution_time_period": "Hour",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"priority": "High",
|
|
||||||
"response_time": response_time,
|
|
||||||
"response_time_period": "Hour",
|
|
||||||
"resolution_time": resolution_time,
|
|
||||||
"resolution_time_period": "Hour",
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"support_and_resolution": [
|
|
||||||
{
|
|
||||||
"workday": "Monday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"workday": "Tuesday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"workday": "Wednesday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"workday": "Thursday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"workday": "Friday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"workday": "Saturday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"workday": "Sunday",
|
|
||||||
"start_time": "10:00:00",
|
|
||||||
"end_time": "18:00:00",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
|
|
||||||
sl_exists = frappe.db.exists("Service Level", {"service_level": service_level})
|
|
||||||
|
|
||||||
if not sl_exists:
|
|
||||||
sl.insert()
|
|
||||||
return sl
|
|
||||||
else:
|
|
||||||
return frappe.get_doc("Service Level", {"service_level": service_level})
|
|
||||||
|
|
||||||
def get_service_level(service_level):
|
|
||||||
return frappe.get_doc("Service Level", service_level)
|
|
||||||
|
|
||||||
def make_holiday_list():
|
|
||||||
holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List")
|
|
||||||
if not holiday_list:
|
|
||||||
now = frappe.utils.now_datetime()
|
|
||||||
holiday_list = frappe.get_doc({
|
|
||||||
"doctype": "Holiday List",
|
|
||||||
"holiday_list_name": "__Test Holiday List",
|
|
||||||
"from_date": "2019-01-01",
|
|
||||||
"to_date": "2019-12-31",
|
|
||||||
"holidays": [
|
|
||||||
{
|
|
||||||
"description": "Test Holiday 1",
|
|
||||||
"holiday_date": "2019-03-05"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Test Holiday 2",
|
|
||||||
"holiday_date": "2019-03-07"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Test Holiday 3",
|
|
||||||
"holiday_date": "2019-02-11"
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}).insert()
|
|
||||||
|
|
||||||
def create_service_level_for_sla():
|
|
||||||
employee_group = make_employee_group()
|
|
||||||
make_holiday_list()
|
|
||||||
make_priorities()
|
|
||||||
|
|
||||||
# Default Service Level
|
|
||||||
create_service_level("__Test Service Level", "__Test Holiday List", employee_group, 4, 6)
|
|
||||||
|
|
||||||
# Service Level
|
|
||||||
create_service_level("_Test Service Level", "__Test Holiday List", employee_group, 2, 3)
|
|
@ -2,28 +2,15 @@
|
|||||||
// For license information, please see license.txt
|
// For license information, please see license.txt
|
||||||
|
|
||||||
frappe.ui.form.on('Service Level Agreement', {
|
frappe.ui.form.on('Service Level Agreement', {
|
||||||
service_level: function(frm) {
|
setup: function(frm) {
|
||||||
frm.fields_dict.support_and_resolution.grid.remove_all();
|
let allow_statuses = [];
|
||||||
frappe.call({
|
const exclude_statuses = ['Open', 'Closed', 'Resolved'];
|
||||||
"method": "frappe.client.get",
|
|
||||||
args: {
|
frappe.model.with_doctype('Issue', () => {
|
||||||
doctype: "Service Level",
|
let statuses = frappe.meta.get_docfield('Issue', 'status', frm.doc.name).options;
|
||||||
name: frm.doc.service_level
|
statuses = statuses.split('\n');
|
||||||
},
|
allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status));
|
||||||
callback: function(data){
|
frappe.meta.get_docfield('Pause SLA On Status', 'status', frm.doc.name).options = [''].concat(allow_statuses);
|
||||||
let count = Math.max(data.message.priorities.length, data.message.support_and_resolution.length);
|
|
||||||
let i = 0;
|
|
||||||
while (i < count){
|
|
||||||
if (data.message.priorities[i]) {
|
|
||||||
frm.add_child("priorities", data.message.priorities[i]);
|
|
||||||
}
|
|
||||||
if (data.message.support_and_resolution[i]) {
|
|
||||||
frm.add_child("support_and_resolution", data.message.support_and_resolution[i]);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
frm.refresh();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
});
|
});
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"autoname": "format:SLA-{service_level}-{####}",
|
"autoname": "format:SLA-{service_level}-{####}",
|
||||||
"creation": "2018-12-26 21:08:15.448812",
|
"creation": "2018-12-26 21:08:15.448812",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
@ -6,12 +7,13 @@
|
|||||||
"engine": "InnoDB",
|
"engine": "InnoDB",
|
||||||
"field_order": [
|
"field_order": [
|
||||||
"enable",
|
"enable",
|
||||||
|
"section_break_2",
|
||||||
"service_level",
|
"service_level",
|
||||||
|
"default_priority",
|
||||||
"default_service_level_agreement",
|
"default_service_level_agreement",
|
||||||
"holiday_list",
|
|
||||||
"column_break_2",
|
"column_break_2",
|
||||||
"employee_group",
|
"employee_group",
|
||||||
"default_priority",
|
"holiday_list",
|
||||||
"entity_section",
|
"entity_section",
|
||||||
"entity_type",
|
"entity_type",
|
||||||
"column_break_10",
|
"column_break_10",
|
||||||
@ -21,49 +23,40 @@
|
|||||||
"active",
|
"active",
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
"end_date",
|
"end_date",
|
||||||
|
"section_break_18",
|
||||||
|
"pause_sla_on",
|
||||||
"response_and_resolution_time_section",
|
"response_and_resolution_time_section",
|
||||||
"priorities",
|
"priorities",
|
||||||
"support_and_resolution_section_break",
|
"support_and_resolution_section_break",
|
||||||
"support_and_resolution"
|
"support_and_resolution"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
|
||||||
"default": "0",
|
|
||||||
"depends_on": "eval: !doc.customer;",
|
|
||||||
"fieldname": "default_service_level_agreement",
|
|
||||||
"fieldtype": "Check",
|
|
||||||
"label": "Default Service Level Agreement"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "service_level",
|
"fieldname": "service_level",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Data",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Service Level",
|
"label": "Service Level",
|
||||||
"options": "Service Level",
|
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "service_level.holiday_list",
|
|
||||||
"fieldname": "holiday_list",
|
"fieldname": "holiday_list",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"label": "Holiday List",
|
"label": "Holiday List",
|
||||||
"options": "Holiday List",
|
"options": "Holiday List",
|
||||||
"read_only": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "column_break_2",
|
"fieldname": "column_break_2",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fetch_from": "service_level.employee_group",
|
|
||||||
"fieldname": "employee_group",
|
"fieldname": "employee_group",
|
||||||
"fieldtype": "Link",
|
"fieldtype": "Link",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"in_standard_filter": 1,
|
"in_standard_filter": 1,
|
||||||
"label": "Employee Group",
|
"label": "Employee Group",
|
||||||
"options": "Employee Group",
|
"options": "Employee Group"
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "agreement_details_section",
|
"fieldname": "agreement_details_section",
|
||||||
@ -103,21 +96,15 @@
|
|||||||
"fieldname": "support_and_resolution",
|
"fieldname": "support_and_resolution",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Support and Resolution",
|
"label": "Support and Resolution",
|
||||||
"options": "Service Day"
|
"options": "Service Day",
|
||||||
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "priorities",
|
"fieldname": "priorities",
|
||||||
"fieldtype": "Table",
|
"fieldtype": "Table",
|
||||||
"label": "Priorities",
|
"label": "Priorities",
|
||||||
"options": "Service Level Priority"
|
"options": "Service Level Priority",
|
||||||
},
|
"reqd": 1
|
||||||
{
|
|
||||||
"fetch_from": "service_level.default_priority",
|
|
||||||
"fieldname": "default_priority",
|
|
||||||
"fieldtype": "Link",
|
|
||||||
"label": "Default Priority",
|
|
||||||
"options": "Issue Priority",
|
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "1",
|
"default": "1",
|
||||||
@ -156,9 +143,38 @@
|
|||||||
"fieldname": "enable",
|
"fieldname": "enable",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Enable"
|
"label": "Enable"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_2",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "default_service_level_agreement",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Default Service Level Agreement"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "default_priority",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Default Priority",
|
||||||
|
"options": "Issue Priority",
|
||||||
|
"read_only": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_18",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"hide_border": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "pause_sla_on",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Pause SLA On",
|
||||||
|
"options": "Pause SLA On Status"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2019-07-09 17:22:16.402939",
|
"links": [],
|
||||||
|
"modified": "2020-06-10 12:30:15.050785",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Service Level Agreement",
|
"name": "Service Level Agreement",
|
||||||
|
@ -6,11 +6,73 @@ from __future__ import unicode_literals
|
|||||||
import frappe
|
import frappe
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate, get_weekdays
|
||||||
|
|
||||||
class ServiceLevelAgreement(Document):
|
class ServiceLevelAgreement(Document):
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.validate_doc()
|
||||||
|
self.check_priorities()
|
||||||
|
self.check_support_and_resolution()
|
||||||
|
|
||||||
|
def check_priorities(self):
|
||||||
|
default_priority = []
|
||||||
|
priorities = []
|
||||||
|
|
||||||
|
for priority in self.priorities:
|
||||||
|
# Check if response and resolution time is set for every priority
|
||||||
|
if not (priority.response_time or priority.resolution_time):
|
||||||
|
frappe.throw(_("Set Response Time and Resolution for Priority {0} at index {1}.").format(priority.priority, priority.idx))
|
||||||
|
|
||||||
|
priorities.append(priority.priority)
|
||||||
|
|
||||||
|
if priority.default_priority:
|
||||||
|
default_priority.append(priority.default_priority)
|
||||||
|
|
||||||
|
response = priority.response_time
|
||||||
|
resolution = priority.resolution_time
|
||||||
|
|
||||||
|
if response > resolution:
|
||||||
|
frappe.throw(_("Response Time for {0} at index {1} can't be greater than Resolution Time.").format(priority.priority, priority.idx))
|
||||||
|
|
||||||
|
# Check if repeated priority
|
||||||
|
if not len(set(priorities)) == len(priorities):
|
||||||
|
repeated_priority = get_repeated(priorities)
|
||||||
|
frappe.throw(_("Priority {0} has been repeated.").format(repeated_priority))
|
||||||
|
|
||||||
|
# Check if repeated default priority
|
||||||
|
if not len(set(default_priority)) == len(default_priority):
|
||||||
|
frappe.throw(_("Select only one Priority as Default."))
|
||||||
|
|
||||||
|
# set default priority from priorities
|
||||||
|
try:
|
||||||
|
self.default_priority = next(d.priority for d in self.priorities if d.default_priority)
|
||||||
|
except Exception:
|
||||||
|
frappe.throw(_("Select a Default Priority."))
|
||||||
|
|
||||||
|
def check_support_and_resolution(self):
|
||||||
|
week = get_weekdays()
|
||||||
|
support_days = []
|
||||||
|
|
||||||
|
for support_and_resolution in self.support_and_resolution:
|
||||||
|
# Check if start and end time is set for every support day
|
||||||
|
if not (support_and_resolution.start_time or support_and_resolution.end_time):
|
||||||
|
frappe.throw(_("Set Start Time and End Time for \
|
||||||
|
Support Day {0} at index {1}.".format(support_and_resolution.workday, support_and_resolution.idx)))
|
||||||
|
|
||||||
|
support_days.append(support_and_resolution.workday)
|
||||||
|
support_and_resolution.idx = week.index(support_and_resolution.workday) + 1
|
||||||
|
|
||||||
|
if support_and_resolution.start_time >= support_and_resolution.end_time:
|
||||||
|
frappe.throw(_("Start Time can't be greater than or equal to End Time \
|
||||||
|
for {0}.".format(support_and_resolution.workday)))
|
||||||
|
|
||||||
|
# Check for repeated workday
|
||||||
|
if not len(set(support_days)) == len(support_days):
|
||||||
|
repeated_days = get_repeated(support_days)
|
||||||
|
frappe.throw(_("Workday {0} has been repeated.").format(repeated_days))
|
||||||
|
|
||||||
|
def validate_doc(self):
|
||||||
if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
|
if not frappe.db.get_single_value("Support Settings", "track_service_level_agreement"):
|
||||||
frappe.throw(_("Service Level Agreement tracking is not enabled."))
|
frappe.throw(_("Service Level Agreement tracking is not enabled."))
|
||||||
|
|
||||||
@ -35,9 +97,7 @@ class ServiceLevelAgreement(Document):
|
|||||||
return frappe._dict({
|
return frappe._dict({
|
||||||
"priority": priority.priority,
|
"priority": priority.priority,
|
||||||
"response_time": priority.response_time,
|
"response_time": priority.response_time,
|
||||||
"response_time_period": priority.response_time_period,
|
"resolution_time": priority.resolution_time
|
||||||
"resolution_time": priority.resolution_time,
|
|
||||||
"resolution_time_period": priority.resolution_time_period
|
|
||||||
})
|
})
|
||||||
|
|
||||||
def check_agreement_status():
|
def check_agreement_status():
|
||||||
@ -111,3 +171,14 @@ def get_service_level_agreement_filters(name, customer=None):
|
|||||||
"priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])],
|
"priority": [priority.priority for priority in frappe.get_list("Service Level Priority", filters={"parent": name}, fields=["priority"])],
|
||||||
"service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)]
|
"service_level_agreements": [d.name for d in frappe.get_list("Service Level Agreement", filters=filters, or_filters=or_filters)]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def get_repeated(values):
|
||||||
|
unique_list = []
|
||||||
|
diff = []
|
||||||
|
for value in values:
|
||||||
|
if value not in unique_list:
|
||||||
|
unique_list.append(str(value))
|
||||||
|
else:
|
||||||
|
if value not in diff:
|
||||||
|
diff.append(str(value))
|
||||||
|
return " ".join(diff)
|
||||||
|
@ -5,19 +5,20 @@ from __future__ import unicode_literals
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
import unittest
|
import unittest
|
||||||
from erpnext.support.doctype.service_level.test_service_level import create_service_level_for_sla
|
from erpnext.hr.doctype.employee_group.test_employee_group import make_employee_group
|
||||||
|
from erpnext.support.doctype.issue_priority.test_issue_priority import make_priorities
|
||||||
|
|
||||||
class TestServiceLevelAgreement(unittest.TestCase):
|
class TestServiceLevelAgreement(unittest.TestCase):
|
||||||
|
def setUp(self):
|
||||||
def test_service_level_agreement(self):
|
frappe.db.sql("delete from `tabService Level Agreement`")
|
||||||
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
|
frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1)
|
||||||
|
|
||||||
create_service_level_for_sla()
|
def test_service_level_agreement(self):
|
||||||
|
|
||||||
# Default Service Level Agreement
|
# Default Service Level Agreement
|
||||||
create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1,
|
create_default_service_level_agreement = create_service_level_agreement(default_service_level_agreement=1,
|
||||||
service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
||||||
entity_type=None, entity=None, response_time=4, resolution_time=6)
|
entity_type=None, entity=None, response_time=14400, resolution_time=21600)
|
||||||
|
|
||||||
get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1)
|
get_default_service_level_agreement = get_service_level_agreement(default_service_level_agreement=1)
|
||||||
|
|
||||||
self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name)
|
self.assertEqual(create_default_service_level_agreement.name, get_default_service_level_agreement.name)
|
||||||
@ -28,8 +29,8 @@ class TestServiceLevelAgreement(unittest.TestCase):
|
|||||||
# Service Level Agreement for Customer
|
# Service Level Agreement for Customer
|
||||||
customer = create_customer()
|
customer = create_customer()
|
||||||
create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
|
create_customer_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
|
||||||
service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
||||||
entity_type="Customer", entity=customer, response_time=2, resolution_time=3)
|
entity_type="Customer", entity=customer, response_time=7200, resolution_time=10800)
|
||||||
get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer)
|
get_customer_service_level_agreement = get_service_level_agreement(entity_type="Customer", entity=customer)
|
||||||
|
|
||||||
self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name)
|
self.assertEqual(create_customer_service_level_agreement.name, get_customer_service_level_agreement.name)
|
||||||
@ -40,8 +41,8 @@ class TestServiceLevelAgreement(unittest.TestCase):
|
|||||||
# Service Level Agreement for Customer Group
|
# Service Level Agreement for Customer Group
|
||||||
customer_group = create_customer_group()
|
customer_group = create_customer_group()
|
||||||
create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
|
create_customer_group_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
|
||||||
service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
||||||
entity_type="Customer Group", entity=customer_group, response_time=2, resolution_time=3)
|
entity_type="Customer Group", entity=customer_group, response_time=7200, resolution_time=10800)
|
||||||
get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group)
|
get_customer_group_service_level_agreement = get_service_level_agreement(entity_type="Customer Group", entity=customer_group)
|
||||||
|
|
||||||
self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name)
|
self.assertEqual(create_customer_group_service_level_agreement.name, get_customer_group_service_level_agreement.name)
|
||||||
@ -52,8 +53,8 @@ class TestServiceLevelAgreement(unittest.TestCase):
|
|||||||
# Service Level Agreement for Territory
|
# Service Level Agreement for Territory
|
||||||
territory = create_territory()
|
territory = create_territory()
|
||||||
create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
|
create_territory_service_level_agreement = create_service_level_agreement(default_service_level_agreement=0,
|
||||||
service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
||||||
entity_type="Territory", entity=territory, response_time=2, resolution_time=3)
|
entity_type="Territory", entity=territory, response_time=7200, resolution_time=10800)
|
||||||
get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory)
|
get_territory_service_level_agreement = get_service_level_agreement(entity_type="Territory", entity=territory)
|
||||||
|
|
||||||
self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name)
|
self.assertEqual(create_territory_service_level_agreement.name, get_territory_service_level_agreement.name)
|
||||||
@ -71,14 +72,19 @@ def get_service_level_agreement(default_service_level_agreement=None, entity_typ
|
|||||||
service_level_agreement = frappe.get_doc("Service Level Agreement", filters)
|
service_level_agreement = frappe.get_doc("Service Level Agreement", filters)
|
||||||
return service_level_agreement
|
return service_level_agreement
|
||||||
|
|
||||||
def create_service_level_agreement(default_service_level_agreement, service_level, holiday_list, employee_group,
|
def create_service_level_agreement(default_service_level_agreement, holiday_list, employee_group,
|
||||||
response_time, entity_type, entity, resolution_time):
|
response_time, entity_type, entity, resolution_time):
|
||||||
|
|
||||||
|
employee_group = make_employee_group()
|
||||||
|
make_holiday_list()
|
||||||
|
make_priorities()
|
||||||
|
|
||||||
service_level_agreement = frappe.get_doc({
|
service_level_agreement = frappe.get_doc({
|
||||||
"doctype": "Service Level Agreement",
|
"doctype": "Service Level Agreement",
|
||||||
"enable": 1,
|
"enable": 1,
|
||||||
|
"service_level": "__Test Service Level",
|
||||||
"default_service_level_agreement": default_service_level_agreement,
|
"default_service_level_agreement": default_service_level_agreement,
|
||||||
"service_level": service_level,
|
"default_priority": "Medium",
|
||||||
"holiday_list": holiday_list,
|
"holiday_list": holiday_list,
|
||||||
"employee_group": employee_group,
|
"employee_group": employee_group,
|
||||||
"entity_type": entity_type,
|
"entity_type": entity_type,
|
||||||
@ -109,6 +115,11 @@ def create_service_level_agreement(default_service_level_agreement, service_leve
|
|||||||
"resolution_time_period": "Hour",
|
"resolution_time_period": "Hour",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"pause_sla_on": [
|
||||||
|
{
|
||||||
|
"status": "Replied"
|
||||||
|
}
|
||||||
|
],
|
||||||
"support_and_resolution": [
|
"support_and_resolution": [
|
||||||
{
|
{
|
||||||
"workday": "Monday",
|
"workday": "Monday",
|
||||||
@ -167,6 +178,7 @@ def create_service_level_agreement(default_service_level_agreement, service_leve
|
|||||||
else:
|
else:
|
||||||
return frappe.get_doc("Service Level Agreement", service_level_agreement_exists)
|
return frappe.get_doc("Service Level Agreement", service_level_agreement_exists)
|
||||||
|
|
||||||
|
|
||||||
def create_customer():
|
def create_customer():
|
||||||
customer = frappe.get_doc({
|
customer = frappe.get_doc({
|
||||||
"doctype": "Customer",
|
"doctype": "Customer",
|
||||||
@ -206,23 +218,41 @@ def create_territory():
|
|||||||
return frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"})
|
return frappe.db.exists("Territory", {"territory_name": "_Test SLA Territory"})
|
||||||
|
|
||||||
def create_service_level_agreements_for_issues():
|
def create_service_level_agreements_for_issues():
|
||||||
create_service_level_for_sla()
|
create_service_level_agreement(default_service_level_agreement=1, holiday_list="__Test Holiday List",
|
||||||
|
employee_group="_Test Employee Group", entity_type=None, entity=None, response_time=14400, resolution_time=21600)
|
||||||
create_service_level_agreement(default_service_level_agreement=1,
|
|
||||||
service_level="__Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
|
||||||
entity_type=None, entity=None, response_time=4, resolution_time=6)
|
|
||||||
|
|
||||||
create_customer()
|
create_customer()
|
||||||
create_service_level_agreement(default_service_level_agreement=0,
|
create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
|
||||||
service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
employee_group="_Test Employee Group", entity_type="Customer", entity="_Test Customer", response_time=7200, resolution_time=10800)
|
||||||
entity_type="Customer", entity="_Test Customer", response_time=2, resolution_time=3)
|
|
||||||
|
|
||||||
create_customer_group()
|
create_customer_group()
|
||||||
create_service_level_agreement(default_service_level_agreement=0,
|
create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
|
||||||
service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
employee_group="_Test Employee Group", entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=7200, resolution_time=10800)
|
||||||
entity_type="Customer Group", entity="_Test SLA Customer Group", response_time=2, resolution_time=3)
|
|
||||||
|
|
||||||
create_territory()
|
create_territory()
|
||||||
create_service_level_agreement(default_service_level_agreement=0,
|
create_service_level_agreement(default_service_level_agreement=0, holiday_list="__Test Holiday List",
|
||||||
service_level="_Test Service Level", holiday_list="__Test Holiday List", employee_group="_Test Employee Group",
|
employee_group="_Test Employee Group", entity_type="Territory", entity="_Test SLA Territory", response_time=7200, resolution_time=10800)
|
||||||
entity_type="Territory", entity="_Test SLA Territory", response_time=2, resolution_time=3)
|
|
||||||
|
def make_holiday_list():
|
||||||
|
holiday_list = frappe.db.exists("Holiday List", "__Test Holiday List")
|
||||||
|
if not holiday_list:
|
||||||
|
holiday_list = frappe.get_doc({
|
||||||
|
"doctype": "Holiday List",
|
||||||
|
"holiday_list_name": "__Test Holiday List",
|
||||||
|
"from_date": "2019-01-01",
|
||||||
|
"to_date": "2019-12-31",
|
||||||
|
"holidays": [
|
||||||
|
{
|
||||||
|
"description": "Test Holiday 1",
|
||||||
|
"holiday_date": "2019-03-05"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Test Holiday 2",
|
||||||
|
"holiday_date": "2019-03-07"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "Test Holiday 3",
|
||||||
|
"holiday_date": "2019-02-11"
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}).insert()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2019-05-04 05:54:03.658991",
|
"creation": "2019-05-04 05:54:03.658991",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@ -9,10 +10,8 @@
|
|||||||
"default_priority",
|
"default_priority",
|
||||||
"sb_00",
|
"sb_00",
|
||||||
"response_time",
|
"response_time",
|
||||||
"response_time_period",
|
|
||||||
"cb_00",
|
"cb_00",
|
||||||
"resolution_time",
|
"resolution_time"
|
||||||
"resolution_time_period"
|
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@ -28,16 +27,11 @@
|
|||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"columns": 1,
|
"columns": 2,
|
||||||
"fieldname": "response_time",
|
|
||||||
"fieldtype": "Int",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Response Time"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 1,
|
|
||||||
"fieldname": "resolution_time",
|
"fieldname": "resolution_time",
|
||||||
"fieldtype": "Int",
|
"fieldtype": "Duration",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Resolution Time"
|
"label": "Resolution Time"
|
||||||
},
|
},
|
||||||
@ -45,36 +39,31 @@
|
|||||||
"fieldname": "cb_00",
|
"fieldname": "cb_00",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "response_time_period",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Response Time Period",
|
|
||||||
"options": "Hour\nDay\nWeek"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"columns": 2,
|
|
||||||
"fieldname": "resolution_time_period",
|
|
||||||
"fieldtype": "Select",
|
|
||||||
"in_list_view": 1,
|
|
||||||
"label": "Resolution Time Period",
|
|
||||||
"options": "Hour\nDay\nWeek"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"fieldname": "cb_01",
|
"fieldname": "cb_01",
|
||||||
"fieldtype": "Column Break"
|
"fieldtype": "Column Break"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
"columns": 1,
|
||||||
"default": "0",
|
"default": "0",
|
||||||
"fieldname": "default_priority",
|
"fieldname": "default_priority",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Default Priority"
|
"label": "Default Priority"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"columns": 2,
|
||||||
|
"fieldname": "response_time",
|
||||||
|
"fieldtype": "Duration",
|
||||||
|
"hide_days": 1,
|
||||||
|
"hide_seconds": 1,
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "First Response Time"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"modified": "2019-05-21 06:54:42.674377",
|
"links": [],
|
||||||
|
"modified": "2020-06-10 12:45:47.545915",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Service Level Priority",
|
"name": "Service Level Priority",
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Support Settings', {
|
frappe.ui.form.on('Support Settings', {
|
||||||
refresh: function(frm) {
|
refresh: function(frm) {
|
||||||
|
//
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
|
"actions": [],
|
||||||
"creation": "2017-02-17 13:07:35.686409",
|
"creation": "2017-02-17 13:07:35.686409",
|
||||||
"doctype": "DocType",
|
"doctype": "DocType",
|
||||||
"editable_grid": 1,
|
"editable_grid": 1,
|
||||||
@ -122,13 +123,15 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.track_service_level_agreement;",
|
||||||
"fieldname": "allow_resetting_service_level_agreement",
|
"fieldname": "allow_resetting_service_level_agreement",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Allow Resetting Service Level Agreement"
|
"label": "Allow Resetting Service Level Agreement"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"modified": "2019-07-10 22:52:39.663873",
|
"links": [],
|
||||||
|
"modified": "2020-06-05 17:56:17.491684",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Support",
|
"module": "Support",
|
||||||
"name": "Support Settings",
|
"name": "Support Settings",
|
||||||
|
@ -193,14 +193,17 @@ class ItemConfigure {
|
|||||||
filtered_items_count === 1 ?
|
filtered_items_count === 1 ?
|
||||||
filtered_items[0] : '';
|
filtered_items[0] : '';
|
||||||
|
|
||||||
|
// Allow Add to Cart if adding out of stock items enabled in Shopping Cart else check stock.
|
||||||
|
const in_stock = product_info.allow_items_not_in_stock ? 1 : product_info.in_stock;
|
||||||
|
const add_to_cart = `<a href data-action="btn_add_to_cart" data-item-code="${one_item}">${__('Add to cart')}</a>`;
|
||||||
|
const product_action = in_stock ? add_to_cart : `<a style="color:#74808b;">${__('Not in Stock')}</a>`;
|
||||||
|
|
||||||
const item_add_to_cart = one_item ? `
|
const item_add_to_cart = one_item ? `
|
||||||
<div class="alert alert-success d-flex justify-content-between align-items-center" role="alert">
|
<div class="alert alert-success d-flex justify-content-between align-items-center" role="alert">
|
||||||
<div>
|
<div>
|
||||||
<div>${one_item} ${product_info && product_info.price ? '(' + product_info.price.formatted_price_sales_uom + ')' : ''}</div>
|
<div>${one_item} ${product_info && product_info.price ? '(' + product_info.price.formatted_price_sales_uom + ')' : ''}</div>
|
||||||
</div>
|
</div>
|
||||||
<a href data-action="btn_add_to_cart" data-item-code="${one_item}">
|
${product_action}
|
||||||
${__('Add to cart')}
|
|
||||||
</a>
|
|
||||||
</div>
|
</div>
|
||||||
`: '';
|
`: '';
|
||||||
|
|
||||||
|
@ -16,10 +16,10 @@
|
|||||||
<div>{{ introduction }}</div>
|
<div>{{ introduction }}</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{%- if application_form_route -%}
|
{%- if doc.enable_admission_application -%}
|
||||||
<p>
|
<p>
|
||||||
<a class='btn btn-primary'
|
<a class='btn btn-primary'
|
||||||
href='/{{ doc.application_form_route }}'>
|
href='/student-applicant'>
|
||||||
{{ _("Apply Now") }}</a>
|
{{ _("Apply Now") }}</a>
|
||||||
</p>
|
</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-check"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<h5 class="card-title">{{ address.name }}</h5>
|
<h5 class="card-title">{{ address.title }}</h5>
|
||||||
<p class="card-text text-muted">
|
<p class="card-text text-muted">
|
||||||
{{ address.display }}
|
{{ address.display }}
|
||||||
</p>
|
</p>
|
||||||
|
@ -109,7 +109,7 @@ frappe.ready(() => {
|
|||||||
reqd: 1
|
reqd: 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: __('Pin Code'),
|
label: __('Postal Code'),
|
||||||
fieldname: 'pincode',
|
fieldname: 'pincode',
|
||||||
fieldtype: 'Data'
|
fieldtype: 'Data'
|
||||||
},
|
},
|
||||||
|
Loading…
x
Reference in New Issue
Block a user