Merge branch 'develop' into link-to-mr
diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py
index 2c15144..c801cfc 100644
--- a/erpnext/accounts/doctype/account/account.py
+++ b/erpnext/accounts/doctype/account/account.py
@@ -101,7 +101,7 @@
return
if not frappe.db.get_value("Account",
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
- frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
+ frappe.throw(_("Please add the account to root level Company - {}").format(ancestors[0]))
elif self.parent_account:
descendants = get_descendants_of('Company', self.company)
if not descendants: return
@@ -164,9 +164,19 @@
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
for company in descendants:
+ company_bold = frappe.bold(company)
+ parent_acc_name_bold = frappe.bold(parent_acc_name)
if not parent_acc_name_map.get(company):
- frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
- .format(company, parent_acc_name))
+ frappe.throw(_("While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
+ .format(company_bold, parent_acc_name_bold), title=_("Account Not Found"))
+
+ # validate if parent of child company account to be added is a group
+ if (frappe.db.get_value("Account", self.parent_account, "is_group")
+ and not frappe.db.get_value("Account", parent_acc_name_map[company], "is_group")):
+ msg = _("While creating account for Child Company {0}, parent account {1} found as a ledger account.").format(company_bold, parent_acc_name_bold)
+ msg += "<br><br>"
+ msg += _("Please convert the parent account in corresponding child company to a group account.")
+ frappe.throw(msg, title=_("Invalid Parent Account"))
filters = {
"account_name": self.account_name,
@@ -309,8 +319,9 @@
allow_child_account_creation = _("Allow Account Creation Against Child Company")
message = _("Account {0} exists in parent company {1}.").format(frappe.bold(old_acc_name), frappe.bold(ancestor))
- message += "<br>" + _("Renaming it is only allowed via parent company {0}, \
- to avoid mismatch.").format(frappe.bold(ancestor)) + "<br><br>"
+ message += "<br>"
+ message += _("Renaming it is only allowed via parent company {0}, to avoid mismatch.").format(frappe.bold(ancestor))
+ message += "<br><br>"
message += _("To overrule this, enable '{0}' in company {1}").format(allow_child_account_creation, frappe.bold(account.company))
frappe.throw(message, title=_("Rename Not Allowed"))
diff --git a/erpnext/accounts/doctype/account/test_account.py b/erpnext/accounts/doctype/account/test_account.py
index b6a950b..0605d89 100644
--- a/erpnext/accounts/doctype/account/test_account.py
+++ b/erpnext/accounts/doctype/account/test_account.py
@@ -111,6 +111,17 @@
self.assertEqual(acc_tc_4, "Test Sync Account - _TC4")
self.assertEqual(acc_tc_5, "Test Sync Account - _TC5")
+ def test_add_account_to_a_group(self):
+ frappe.db.set_value("Account", "Office Rent - _TC3", "is_group", 1)
+
+ acc = frappe.new_doc("Account")
+ acc.account_name = "Test Group Account"
+ acc.parent_account = "Office Rent - _TC3"
+ acc.company = "_Test Company 3"
+ self.assertRaises(frappe.ValidationError, acc.insert)
+
+ frappe.db.set_value("Account", "Office Rent - _TC3", "is_group", 0)
+
def test_account_rename_sync(self):
frappe.local.flags.pop("ignore_root_company_validation", None)
@@ -160,6 +171,7 @@
for doc in to_delete:
frappe.delete_doc("Account", doc)
+
def _make_test_records(verbose):
from frappe.test_runner import make_test_objects
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
index 3c12f85..9a6c389 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js
@@ -7,7 +7,7 @@
frm.set_query('document_type', () => {
let invalid_doctypes = frappe.model.core_doctypes_list;
invalid_doctypes.push('Accounting Dimension', 'Project',
- 'Cost Center', 'Accounting Dimension Detail');
+ 'Cost Center', 'Accounting Dimension Detail', 'Company');
return {
filters: {
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 8834385..f888d9e 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -19,7 +19,7 @@
def validate(self):
if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project',
- 'Cost Center', 'Accounting Dimension Detail') :
+ 'Cost Center', 'Accounting Dimension Detail', 'Company') :
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg)
diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json
index 4e22218..570111a 100644
--- a/erpnext/accounts/doctype/pos_profile/pos_profile.json
+++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json
@@ -14,6 +14,7 @@
"column_break_9",
"update_stock",
"ignore_pricing_rule",
+ "hide_unavailable_items",
"warehouse",
"campaign",
"company_address",
@@ -307,13 +308,19 @@
"fieldtype": "Check",
"label": "Update Stock",
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "hide_unavailable_items",
+ "fieldtype": "Check",
+ "label": "Hide Unavailable Items"
}
],
"icon": "icon-cog",
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-10-20 13:16:50.665081",
+ "modified": "2020-10-29 13:18:38.795925",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
index c681c89..cc8ed4b 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json
@@ -504,10 +504,10 @@
},
{
"default": "0",
- "depends_on": "eval:in_list(['Discount Percentage', 'Discount Amount'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
+ "depends_on": "eval:in_list(['Discount Percentage'], doc.rate_or_discount) && doc.apply_multiple_pricing_rules",
"fieldname": "apply_discount_on_rate",
"fieldtype": "Check",
- "label": "Apply Discount on Rate"
+ "label": "Apply Discount on Discounted Rate"
},
{
"default": "0",
@@ -563,7 +563,7 @@
"icon": "fa fa-gift",
"idx": 1,
"links": [],
- "modified": "2020-08-26 12:24:44.740734",
+ "modified": "2020-10-28 16:53:14.416172",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 454776e..149c476 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -60,6 +60,15 @@
if self.price_or_product_discount == 'Price' and not self.rate_or_discount:
throw(_("Rate or Discount is required for the price discount."), frappe.MandatoryError)
+ if self.apply_discount_on_rate:
+ if not self.priority:
+ throw(_("As the field {0} is enabled, the field {1} is mandatory.")
+ .format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")))
+
+ if self.priority and cint(self.priority) == 1:
+ throw(_("As the field {0} is enabled, the value of the field {1} should be more than 1.")
+ .format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")))
+
def validate_applicable_for_selling_or_buying(self):
if not self.selling and not self.buying:
throw(_("Atleast one of the Selling or Buying must be selected"))
@@ -226,12 +235,11 @@
item_details = frappe._dict({
"doctype": args.doctype,
+ "has_margin": False,
"name": args.name,
"parent": args.parent,
"parenttype": args.parenttype,
- "child_docname": args.get('child_docname'),
- "discount_percentage_on_rate": [],
- "discount_amount_on_rate": []
+ "child_docname": args.get('child_docname')
})
if args.ignore_pricing_rule or not args.item_code:
@@ -279,6 +287,10 @@
else:
get_product_discount_rule(pricing_rule, item_details, args, doc)
+ if not item_details.get("has_margin"):
+ item_details.margin_type = None
+ item_details.margin_rate_or_amount = 0.0
+
item_details.has_pricing_rule = 1
item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules])
@@ -330,13 +342,11 @@
def apply_price_discount_rule(pricing_rule, item_details, args):
item_details.pricing_rule_for = pricing_rule.rate_or_discount
- if ((pricing_rule.margin_type == 'Amount' and pricing_rule.currency == args.currency)
+ if ((pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == args.currency)
or (pricing_rule.margin_type == 'Percentage')):
item_details.margin_type = pricing_rule.margin_type
item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
- else:
- item_details.margin_type = None
- item_details.margin_rate_or_amount = 0.0
+ item_details.has_margin = True
if pricing_rule.rate_or_discount == 'Rate':
pricing_rule_rate = 0.0
@@ -351,9 +361,9 @@
if pricing_rule.rate_or_discount != apply_on: continue
field = frappe.scrub(apply_on)
- if pricing_rule.apply_discount_on_rate:
- discount_field = "{0}_on_rate".format(field)
- item_details[discount_field].append(pricing_rule.get(field, 0))
+ if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"):
+ # Apply discount on discounted rate
+ item_details[field] += ((100 - item_details[field]) * (pricing_rule.get(field, 0) / 100))
else:
if field not in item_details:
item_details.setdefault(field, 0)
@@ -361,14 +371,6 @@
item_details[field] += (pricing_rule.get(field, 0)
if pricing_rule else args.get(field, 0))
-def set_discount_amount(rate, item_details):
- for field in ['discount_percentage_on_rate', 'discount_amount_on_rate']:
- for d in item_details.get(field):
- dis_amount = (rate * d / 100
- if field == 'discount_percentage_on_rate' else d)
- rate -= dis_amount
- item_details.rate = rate
-
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None):
from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
get_pricing_rule_items)
diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
index 3555ca8..22a031c 100644
--- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py
@@ -385,7 +385,7 @@
so.load_from_db()
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item 2")
-
+
def test_cumulative_pricing_rule(self):
frappe.delete_doc_if_exists('Pricing Rule', '_Test Cumulative Pricing Rule')
test_record = {
@@ -429,34 +429,61 @@
details = get_item_details(args)
self.assertTrue(details)
-
+
def test_pricing_rule_for_condition(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
-
+
make_pricing_rule(selling=1, margin_type="Percentage", \
condition="customer=='_Test Customer 1' and is_return==0", discount_percentage=10)
-
+
# Incorrect Customer and Correct is_return value
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 2", is_return=0)
si.items[0].price_list_rate = 1000
si.submit()
item = si.items[0]
self.assertEquals(item.rate, 100)
-
+
# Correct Customer and Incorrect is_return value
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=1, qty=-1)
si.items[0].price_list_rate = 1000
si.submit()
item = si.items[0]
self.assertEquals(item.rate, 100)
-
+
# Correct Customer and correct is_return value
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", is_return=0)
si.items[0].price_list_rate = 1000
si.submit()
item = si.items[0]
self.assertEquals(item.rate, 900)
-
+
+ def test_multiple_pricing_rules(self):
+ make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
+ title="_Test Pricing Rule 1")
+ make_pricing_rule(discount_percentage=10, selling=1, title="_Test Pricing Rule 2", priority=2,
+ apply_multiple_pricing_rules=1)
+ si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
+ self.assertEqual(si.items[0].discount_percentage, 30)
+ si.delete()
+
+ frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
+ frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
+
+ def test_multiple_pricing_rules_with_apply_discount_on_discounted_rate(self):
+ frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
+
+ make_pricing_rule(discount_percentage=20, selling=1, priority=1, apply_multiple_pricing_rules=1,
+ title="_Test Pricing Rule 1")
+ make_pricing_rule(discount_percentage=10, selling=1, priority=2,
+ apply_discount_on_rate=1, title="_Test Pricing Rule 2", apply_multiple_pricing_rules=1)
+
+ si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
+ self.assertEqual(si.items[0].discount_percentage, 28)
+ si.delete()
+
+ frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 1")
+ frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule 2")
+
def make_pricing_rule(**args):
args = frappe._dict(args)
@@ -468,6 +495,7 @@
"applicable_for": args.applicable_for,
"selling": args.selling or 0,
"currency": "USD",
+ "apply_discount_on_rate": args.apply_discount_on_rate or 0,
"buying": args.buying or 0,
"min_qty": args.min_qty or 0.0,
"max_qty": args.max_qty or 0.0,
@@ -476,9 +504,13 @@
"rate": args.rate or 0.0,
"margin_type": args.margin_type,
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0,
- "condition": args.condition or ''
+ "condition": args.condition or '',
+ "apply_multiple_pricing_rules": args.apply_multiple_pricing_rules or 0
})
+ if args.get("priority"):
+ doc.priority = args.get("priority")
+
apply_on = doc.apply_on.replace(' ', '_').lower()
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
doc.append(child_table.get(doc.apply_on), {
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index 25840d4..b003328 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -14,9 +14,8 @@
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor
-from frappe import _, throw
-from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
-
+from frappe import _, bold
+from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money
class MultiplePricingRuleConflict(frappe.ValidationError): pass
@@ -42,6 +41,7 @@
if not pricing_rules: return []
if apply_multiple_pricing_rules(pricing_rules):
+ pricing_rules = sorted_by_priority(pricing_rules)
for pricing_rule in pricing_rules:
pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
if pricing_rule:
@@ -53,6 +53,20 @@
return rules
+def sorted_by_priority(pricing_rules):
+ # If more than one pricing rules, then sort by priority
+ pricing_rules_list = []
+ pricing_rule_dict = {}
+ for pricing_rule in pricing_rules:
+ if not pricing_rule.get("priority"): continue
+
+ pricing_rule_dict.setdefault(cint(pricing_rule.get("priority")), []).append(pricing_rule)
+
+ for key in sorted(pricing_rule_dict):
+ pricing_rules_list.append(pricing_rule_dict.get(key))
+
+ return pricing_rules_list or pricing_rules
+
def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
filtered_pricing_rules = []
if doc:
@@ -284,12 +298,13 @@
fieldname = field
if fieldname:
- msg = _("""If you {0} {1} quantities of the item <b>{2}</b>, the scheme <b>{3}</b>
- will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description)
+ msg = (_("If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.")
+ .format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description)))
if fieldname in ['min_amt', 'max_amt']:
- msg = _("""If you {0} {1} worth item <b>{2}</b>, the scheme <b>{3}</b> will be applied on the item.
- """).format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description)
+ msg = (_("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.")
+ .format(type_of_transaction, fmt_money(args.get(fieldname), currency=args.get("currency")),
+ bold(item_code), bold(args.rule_description)))
frappe.msgprint(msg)
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 811fc35..c17fccd 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -237,7 +237,7 @@
subscription.party_type = 'Customer'
subscription.party = '_Test Customer'
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1})
- subscription.start_date = '2018-01-01'
+ subscription.start_date = add_days(nowdate(), -1000)
subscription.insert()
subscription.process() # generate first invoice
diff --git a/erpnext/accounts/report/pos_register/pos_register.py b/erpnext/accounts/report/pos_register/pos_register.py
index 0bcde64..52f7fe2 100644
--- a/erpnext/accounts/report/pos_register/pos_register.py
+++ b/erpnext/accounts/report/pos_register/pos_register.py
@@ -63,6 +63,7 @@
FROM
`tabPOS Invoice` p {from_sales_invoice_payment}
WHERE
+ p.docstatus = 1 and
{group_by_mop_condition}
{conditions}
ORDER BY
diff --git a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json
index bc2edc9..94debf1 100644
--- a/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json
+++ b/erpnext/assets/dashboard_chart/asset_value_analytics/asset_value_analytics.json
@@ -9,9 +9,9 @@
"filters_json": "{\"status\":\"In Location\",\"filter_based_on\":\"Fiscal Year\",\"period_start_date\":\"2020-04-01\",\"period_end_date\":\"2021-03-31\",\"date_based_on\":\"Purchase Date\",\"group_by\":\"--Select a group--\"}",
"group_by_type": "Count",
"idx": 0,
- "is_public": 0,
+ "is_public": 1,
"is_standard": 1,
- "modified": "2020-07-23 13:53:33.211371",
+ "modified": "2020-10-28 23:15:58.432189",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Value Analytics",
diff --git a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json
index e79d2d7..78611da 100644
--- a/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json
+++ b/erpnext/assets/dashboard_chart/category_wise_asset_value/category_wise_asset_value.json
@@ -8,9 +8,9 @@
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}",
"idx": 0,
- "is_public": 0,
+ "is_public": 1,
"is_standard": 1,
- "modified": "2020-07-23 13:39:32.429240",
+ "modified": "2020-10-28 23:16:16.939070",
"modified_by": "Administrator",
"module": "Assets",
"name": "Category-wise Asset Value",
diff --git a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json
index 481586e..848184c 100644
--- a/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json
+++ b/erpnext/assets/dashboard_chart/location_wise_asset_value/location_wise_asset_value.json
@@ -8,9 +8,9 @@
"dynamic_filters_json": "{\"company\":\"frappe.defaults.get_user_default(\\\"Company\\\")\",\"from_date\":\"frappe.datetime.add_months(frappe.datetime.nowdate(), -12)\",\"to_date\":\"frappe.datetime.nowdate()\"}",
"filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}",
"idx": 0,
- "is_public": 0,
+ "is_public": 1,
"is_standard": 1,
- "modified": "2020-07-23 13:42:44.912551",
+ "modified": "2020-10-28 23:16:07.883312",
"modified_by": "Administrator",
"module": "Assets",
"name": "Location-wise Asset Value",
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index 79fcb95..d422876 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -55,6 +55,7 @@
"fieldtype": "Date",
"in_list_view": 1,
"label": "Depreciation Posting Date",
+ "mandatory_depends_on": "eval:parent.doctype == 'Asset'",
"reqd": 1
},
{
@@ -86,7 +87,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-09-16 12:11:30.631788",
+ "modified": "2020-10-30 15:22:29.119868",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 0181ae7..d568ef1 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -876,7 +876,7 @@
},
{
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
- "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[1].name
+ "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"
},
]
@@ -885,6 +885,10 @@
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
se.submit()
+ # Test po_detail field has value or not
+ for item_row in se.items:
+ self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name)
+
po_doc = frappe.get_doc("Purchase Order", po.name)
for row in po_doc.supplied_items:
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 983cfa8..6108a61 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -263,6 +263,7 @@
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
parent_dict.update({"customer": parent_dict.get("party_name")})
+ self.pricing_rules = []
for item in self.get("items"):
if item.get("item_code"):
args = parent_dict.copy()
@@ -301,6 +302,7 @@
if ret.get("pricing_rules"):
self.apply_pricing_rule_on_items(item, ret)
+ self.set_pricing_rule_details(item, ret)
if self.doctype == "Purchase Invoice":
self.set_expense_account(for_validate)
@@ -322,6 +324,9 @@
if item.get('discount_amount'):
item.rate = item.price_list_rate - item.discount_amount
+ if item.get("apply_discount_on_discounted_rate") and pricing_rule_args.get("rate"):
+ item.rate = pricing_rule_args.get("rate")
+
elif pricing_rule_args.get('free_item_data'):
apply_pricing_rule_for_free_items(self, pricing_rule_args.get('free_item_data'))
@@ -335,6 +340,18 @@
frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}")
.format(item.idx, frappe.bold(title), frappe.bold(item.item_code)))
+ def set_pricing_rule_details(self, item_row, args):
+ pricing_rules = get_applied_pricing_rules(args.get("pricing_rules"))
+ if not pricing_rules: return
+
+ for pricing_rule in pricing_rules:
+ self.append("pricing_rules", {
+ "pricing_rule": pricing_rule,
+ "item_code": item_row.item_code,
+ "child_docname": item_row.name,
+ "rule_applied": True
+ })
+
def set_taxes(self):
if not self.meta.get_field("taxes"):
return
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index f376836..9ee83e3 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -301,7 +301,7 @@
# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
for raw_material in transferred_raw_materials + non_stock_items:
- rm_item_key = (raw_material.rm_item_code, item.purchase_order)
+ rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
consumed_qty = raw_material_data.get('qty', 0)
@@ -910,7 +910,7 @@
purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
for data in purchase_receipt_supplied_items:
- pr_key = (data.rm_item_code, args[0])
+ pr_key = (data.rm_item_code, data.main_item_code, args[0])
if pr_key not in backflushed_raw_materials_map:
backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
"qty": 0.0,
@@ -936,7 +936,7 @@
def get_supplied_items(item_code, purchase_receipt, references):
return frappe.get_all("Purchase Receipt Item Supplied",
- fields=["rm_item_code", "consumed_qty", "serial_no", "batch_no"],
+ fields=["rm_item_code", "main_item_code", "consumed_qty", "serial_no", "batch_no"],
filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
def get_asset_item_details(asset_items):
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 92cfdb7..81d07c1 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -608,16 +608,19 @@
base_rate_with_margin = 0.0
if item.price_list_rate:
if item.pricing_rules and not self.doc.ignore_pricing_rule:
+ has_margin = False
for d in get_applied_pricing_rules(item.pricing_rules):
pricing_rule = frappe.get_cached_doc('Pricing Rule', d)
- if (pricing_rule.margin_type == 'Amount' and pricing_rule.currency == self.doc.currency)\
+ if (pricing_rule.margin_type in ['Amount', 'Percentage'] and pricing_rule.currency == self.doc.currency)\
or (pricing_rule.margin_type == 'Percentage'):
item.margin_type = pricing_rule.margin_type
item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
- else:
- item.margin_type = None
- item.margin_rate_or_amount = 0.0
+ has_margin = True
+
+ if not has_margin:
+ item.margin_type = None
+ item.margin_rate_or_amount = 0.0
if item.margin_type and item.margin_rate_or_amount:
margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
diff --git a/erpnext/education/doctype/student_attendance/student_attendance.py b/erpnext/education/doctype/student_attendance/student_attendance.py
index 595dff9..72a8f55 100644
--- a/erpnext/education/doctype/student_attendance/student_attendance.py
+++ b/erpnext/education/doctype/student_attendance/student_attendance.py
@@ -75,6 +75,6 @@
})
if attendance_record:
- record = get_link_to_form('Attendance Record', attendance_record)
+ record = get_link_to_form('Student Attendance', attendance_record)
frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
.format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 1a64bc5..b723387 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -56,23 +56,35 @@
refresh: function(frm) {
if (frm.doc.docstatus === 1) {
frm.trigger("show_progress");
+
+ if (frm.doc.status !== "Completed") {
+ if (frm.doc.po_items && frm.doc.status !== "Closed") {
+ frm.add_custom_button(__("Work Order"), ()=> {
+ frm.trigger("make_work_order");
+ }, __('Create'));
+ }
+
+ if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
+ frm.add_custom_button(__("Material Request"), ()=> {
+ frm.trigger("make_material_request");
+ }, __('Create'));
+ }
+
+ if (frm.doc.status === "Closed") {
+ frm.add_custom_button(__("Re-open"), function() {
+ frm.events.close_open_production_plan(frm, false);
+ }, __("Status"));
+ } else {
+ frm.add_custom_button(__("Close"), function() {
+ frm.events.close_open_production_plan(frm, true);
+ }, __("Status"));
+ }
+ }
}
- if (frm.doc.docstatus === 1 && frm.doc.po_items
- && frm.doc.status != 'Completed') {
- frm.add_custom_button(__("Work Order"), ()=> {
- frm.trigger("make_work_order");
- }, __('Create'));
+ if (frm.doc.status !== "Closed") {
+ frm.page.set_inner_btn_group_as_primary(__('Create'));
}
-
- if (frm.doc.docstatus === 1 && frm.doc.mr_items
- && !in_list(['Material Requested', 'Completed'], frm.doc.status)) {
- frm.add_custom_button(__("Material Request"), ()=> {
- frm.trigger("make_material_request");
- }, __('Create'));
- }
-
- frm.page.set_inner_btn_group_as_primary(__('Create'));
frm.trigger("material_requirement");
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
@@ -121,6 +133,18 @@
set_field_options("projected_qty_formula", projected_qty_formula);
},
+ close_open_production_plan: (frm, close=false) => {
+ frappe.call({
+ method: "set_status",
+ freeze: true,
+ doc: frm.doc,
+ args: {close : close},
+ callback: function() {
+ frm.reload_doc();
+ }
+ });
+ },
+
make_work_order: function(frm) {
frappe.call({
method: "make_work_order",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 90e8b22..850d5ae 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -275,7 +275,7 @@
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
- "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested",
+ "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested",
"print_hide": 1,
"read_only": 1
},
@@ -304,9 +304,10 @@
}
],
"icon": "fa fa-calendar",
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-02-03 00:25:25.934202",
+ "modified": "2020-10-26 13:00:54.335319",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index b6552d5..a314a15 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -219,13 +219,17 @@
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
frappe.delete_doc('Work Order', d.name)
- def set_status(self):
+ def set_status(self, close=None):
self.status = {
0: 'Draft',
1: 'Submitted',
2: 'Cancelled'
}.get(self.docstatus)
+ if close:
+ self.db_set('status', 'Closed')
+ return
+
if self.total_produced_qty > 0:
self.status = "In Process"
if self.total_produced_qty == self.total_planned_qty:
@@ -235,6 +239,9 @@
self.update_ordered_status()
self.update_requested_status()
+ if close is not None:
+ self.db_set('status', self.status)
+
def update_ordered_status(self):
update_status = False
for d in self.po_items:
@@ -735,10 +742,12 @@
mr_items = new_mr_items
if not mr_items:
- frappe.msgprint(_("""As raw materials projected quantity is more than required quantity,
- there is no need to create material request for the warehouse {0}.
- Still if you want to make material request,
- kindly enable <b>Ignore Existing Projected Quantity</b> checkbox""").format(doc.get('for_warehouse')))
+ to_enable = frappe.bold(_("Ignore Existing Projected Quantity"))
+ warehouse = frappe.bold(doc.get('for_warehouse'))
+ message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "<br><br>"
+ message += _(" If you still want to proceed, please enable {0}.").format(to_enable)
+
+ frappe.msgprint(message, title=_("Note"))
return mr_items
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
index d377ef0..165b66f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
@@ -1,6 +1,6 @@
frappe.listview_settings['Production Plan'] = {
add_fields: ["status"],
- filters: [["status", "!=", "Stopped"]],
+ filters: [["status", "!=", "Closed"]],
get_indicator: function(doc) {
if(doc.status==="Submitted") {
return [__("Not Started"), "orange", "status,=,Submitted"];
@@ -10,7 +10,8 @@
"In Process": "orange",
"Completed": "green",
"Material Requested": "darkgrey",
- "Cancelled": "darkgrey"
+ "Cancelled": "darkgrey",
+ "Closed": "grey"
}[doc.status], "status,=," + doc.status];
}
}
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
index 53e33c0..e72f489 100644
--- a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
@@ -11,30 +11,20 @@
{
"fieldname": "warehouse",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Warehouse",
"options": "Warehouse"
}
],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
"links": [],
- "modified": "2020-02-02 10:37:16.650836",
+ "modified": "2020-10-26 12:55:00.778201",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Material Request Warehouse",
"owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
+ "permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8b34eaa..5ef5371 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -732,3 +732,4 @@
erpnext.patches.v13_0.print_uom_after_quantity_patch
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
+erpnext.patches.v13_0.update_reason_for_resignation_in_employee
diff --git a/erpnext/patches/v13_0/rename_issue_doctype_fields.py b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
index 5bd6596..96a6362 100644
--- a/erpnext/patches/v13_0/rename_issue_doctype_fields.py
+++ b/erpnext/patches/v13_0/rename_issue_doctype_fields.py
@@ -53,7 +53,7 @@
# renamed reports from "Minutes to First Response for Issues" to "First Response Time for Issues". Same for Opportunity
for report in ['Minutes to First Response for Issues', 'Minutes to First Response for Opportunity']:
if frappe.db.exists('Report', report):
- frappe.delete_doc('Report', report)
+ frappe.delete_doc('Report', report, ignore_permissions=True)
def convert_to_seconds(value, unit):
diff --git a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
new file mode 100644
index 0000000..792118f
--- /dev/null
+++ b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("hr", "doctype", "employee")
+
+ if frappe.db.has_column("Employee", "reason_for_resignation"):
+ frappe.db.sql(""" UPDATE `tabEmployee`
+ SET reason_for_leaving = reason_for_resignation
+ WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null
+ """)
+
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 23705a8..1358a4b 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -352,9 +352,15 @@
let show_description = function(idx, exist = null) {
if (exist) {
- scan_barcode_field.set_new_description(__('Row #{0}: Qty increased by 1', [idx]));
+ frappe.show_alert({
+ message: __('Row #{0}: Qty increased by 1', [idx]),
+ indicator: 'green'
+ });
} else {
- scan_barcode_field.set_new_description(__('Row #{0}: Item added', [idx]));
+ frappe.show_alert({
+ message: __('Row #{0}: Item added', [idx]),
+ indicator: 'green'
+ });
}
}
@@ -365,7 +371,10 @@
}).then(r => {
const data = r && r.message;
if (!data || Object.keys(data).length === 0) {
- scan_barcode_field.set_new_description(__('Cannot find Item with this barcode'));
+ frappe.show_alert({
+ message: __('Cannot find Item with this Barcode'),
+ indicator: 'red'
+ });
return;
}
@@ -651,7 +660,7 @@
let child = frappe.model.add_child(me.frm.doc, "taxes");
child.charge_type = "On Net Total";
child.account_head = tax;
- child.rate = rate;
+ child.rate = 0;
}
});
}
diff --git a/erpnext/quality_management/desk_page/quality/quality.json b/erpnext/quality_management/desk_page/quality/quality.json
index 5ee7000..7a049b2 100644
--- a/erpnext/quality_management/desk_page/quality/quality.json
+++ b/erpnext/quality_management/desk_page/quality/quality.json
@@ -18,7 +18,7 @@
{
"hidden": 0,
"label": "Review and Action",
- "links": "[\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"description\": \"Non Conformance\",\n \"label\": \"Non Conformance\",\n \"name\": \"Non Conformance\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Review\",\n \"label\": \"Quality Review\",\n \"name\": \"Quality Review\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Quality Action\",\n \"label\": \"Quality Action\",\n \"name\": \"Quality Action\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Modules",
@@ -29,11 +29,11 @@
"docstatus": 0,
"doctype": "Desk Page",
"extends_another_page": 0,
- "icon": "",
+ "hide_custom": 0,
"idx": 0,
"is_standard": 1,
"label": "Quality",
- "modified": "2020-04-01 11:28:51.095012",
+ "modified": "2020-10-27 16:28:54.138055",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality",
@@ -47,6 +47,7 @@
"type": "DocType"
},
{
+ "doc_view": "Tree",
"label": "Quality Procedure",
"link_to": "Quality Procedure",
"type": "DocType"
@@ -55,6 +56,33 @@
"label": "Quality Inspection",
"link_to": "Quality Inspection",
"type": "DocType"
+ },
+ {
+ "color": "#ff8989",
+ "doc_view": "",
+ "format": "{} Open",
+ "label": "Quality Review",
+ "link_to": "Quality Review",
+ "stats_filter": "{\"status\": \"Open\"}",
+ "type": "DocType"
+ },
+ {
+ "color": "#ff8989",
+ "doc_view": "",
+ "format": "{} Open",
+ "label": "Quality Action",
+ "link_to": "Quality Action",
+ "stats_filter": "{\"status\": \"Open\"}",
+ "type": "DocType"
+ },
+ {
+ "color": "#ff8989",
+ "doc_view": "",
+ "format": "{} Open",
+ "label": "Non Conformance",
+ "link_to": "Non Conformance",
+ "stats_filter": "{\"status\": \"Open\"}",
+ "type": "DocType"
}
]
}
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/non_conformance/__init__.py b/erpnext/quality_management/doctype/non_conformance/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/__init__.py
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.js b/erpnext/quality_management/doctype/non_conformance/non_conformance.js
new file mode 100644
index 0000000..e7f5eee
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Non Conformance', {
+ // refresh: function(frm) {
+
+ // }
+});
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.json b/erpnext/quality_management/doctype/non_conformance/non_conformance.json
new file mode 100644
index 0000000..bfeb96b
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.json
@@ -0,0 +1,118 @@
+{
+ "actions": [],
+ "autoname": "format:QA-NC-{#####}",
+ "creation": "2020-10-21 14:49:50.350136",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "subject",
+ "procedure",
+ "process_owner",
+ "full_name",
+ "column_break_4",
+ "status",
+ "section_break_4",
+ "details",
+ "corrective_action",
+ "preventive_action"
+ ],
+ "fields": [
+ {
+ "fieldname": "subject",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Subject",
+ "reqd": 1
+ },
+ {
+ "fieldname": "procedure",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Procedure",
+ "options": "Quality Procedure",
+ "reqd": 1
+ },
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Open\nResolved\nCancelled",
+ "reqd": 1
+ },
+ {
+ "fieldname": "section_break_4",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "details",
+ "fieldtype": "Text Editor",
+ "label": "Details"
+ },
+ {
+ "fetch_from": "procedure.process_owner",
+ "fieldname": "process_owner",
+ "fieldtype": "Data",
+ "label": "Process Owner",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_4",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "process_owner.full_name",
+ "fieldname": "full_name",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Full Name"
+ },
+ {
+ "fieldname": "corrective_action",
+ "fieldtype": "Text",
+ "label": "Corrective Action"
+ },
+ {
+ "fieldname": "preventive_action",
+ "fieldtype": "Text",
+ "label": "Preventive Action"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-10-26 15:27:47.247814",
+ "modified_by": "Administrator",
+ "module": "Quality Management",
+ "name": "Non Conformance",
+ "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": "Employee",
+ "share": 1,
+ "write": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.py b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
new file mode 100644
index 0000000..d4e8cc7
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.py
@@ -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 NonConformance(Document):
+ pass
diff --git a/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
new file mode 100644
index 0000000..54f8b58
--- /dev/null
+++ b/erpnext/quality_management/doctype/non_conformance/test_non_conformance.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+from __future__ import unicode_literals
+
+# import frappe
+import unittest
+
+class TestNonConformance(unittest.TestCase):
+ pass
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.js b/erpnext/quality_management/doctype/quality_action/quality_action.js
index 7078247..e216a75 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.js
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.js
@@ -2,32 +2,5 @@
// For license information, please see license.txt
frappe.ui.form.on('Quality Action', {
- onload: function(frm) {
- frm.set_value("date", frappe.datetime.get_today());
- frm.refresh();
- },
- document_name: function(frm){
- frappe.call({
- "method": "frappe.client.get",
- args: {
- doctype: frm.doc.document_type,
- name: frm.doc.document_name
- },
- callback: function(data){
- frm.fields_dict.resolutions.grid.remove_all();
- let objectives = [];
- if(frm.doc.document_type === "Quality Review"){
- for(let i in data.message.reviews) objectives.push(data.message.reviews[i].review);
- } else {
- for(let j in data.message.parameters) objectives.push(data.message.parameters[j].feedback);
- }
- for (var objective in objectives){
- frm.add_child("resolutions");
- frm.fields_dict.resolutions.get_value()[objective].problem = objectives[objective];
- }
- frm.refresh();
- }
- });
- },
});
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.json b/erpnext/quality_management/doctype/quality_action/quality_action.json
index 8835b47..0cc2a98 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.json
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.json
@@ -1,32 +1,34 @@
{
- "autoname": "format:ACTN-{#####}",
+ "actions": [],
+ "autoname": "format:QA-ACT-{#####}",
"creation": "2018-10-02 11:40:43.666100",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"corrective_preventive",
- "document_type",
- "goal",
+ "review",
+ "feedback",
+ "status",
"cb_00",
"date",
- "document_name",
+ "goal",
"procedure",
- "status",
"sb_00",
"resolutions"
],
"fields": [
{
- "depends_on": "eval:doc.type == 'Quality Review'",
"fetch_from": "review.goal",
"fieldname": "goal",
"fieldtype": "Link",
+ "in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Goal",
- "options": "Quality Goal",
- "read_only": 1
+ "options": "Quality Goal"
},
{
+ "default": "Today",
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
@@ -34,34 +36,20 @@
"read_only": 1
},
{
- "depends_on": "eval:doc.type == 'Quality Review'",
"fieldname": "procedure",
"fieldtype": "Link",
"label": "Procedure",
- "options": "Quality Procedure",
- "read_only": 1
+ "options": "Quality Procedure"
},
{
"default": "Open",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Status",
- "options": "Open\nClosed"
- },
- {
- "fieldname": "document_name",
- "fieldtype": "Dynamic Link",
- "label": "Document Name",
- "options": "document_type"
- },
- {
- "fieldname": "document_type",
- "fieldtype": "Select",
- "in_list_view": 1,
- "label": "Document Type",
- "options": "Quality Review\nQuality Feedback",
- "reqd": 1
+ "options": "Open\nCompleted",
+ "read_only": 1
},
{
"default": "Corrective",
@@ -86,9 +74,24 @@
"fieldtype": "Table",
"label": "Resolutions",
"options": "Quality Action Resolution"
+ },
+ {
+ "fieldname": "review",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Review",
+ "options": "Quality Review"
+ },
+ {
+ "fieldname": "feedback",
+ "fieldtype": "Link",
+ "label": "Feedback",
+ "options": "Quality Feedback"
}
],
- "modified": "2019-05-28 13:10:44.092497",
+ "index_web_pages_for_search": 1,
+ "links": [],
+ "modified": "2020-10-27 16:21:59.533937",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Action",
diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.py b/erpnext/quality_management/doctype/quality_action/quality_action.py
index 88d4bd8..d6fa505 100644
--- a/erpnext/quality_management/doctype/quality_action/quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/quality_action.py
@@ -7,4 +7,5 @@
from frappe.model.document import Document
class QualityAction(Document):
- pass
\ No newline at end of file
+ def validate(self):
+ self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed'
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.js b/erpnext/quality_management/doctype/quality_action/test_quality_action.js
deleted file mode 100644
index 34a8c86..0000000
--- a/erpnext/quality_management/doctype/quality_action/test_quality_action.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Action", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quality Actions
- () => frappe.tests.make('Quality Actions', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_action/test_quality_action.py b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
index 51178d6..24b97ca 100644
--- a/erpnext/quality_management/doctype/quality_action/test_quality_action.py
+++ b/erpnext/quality_management/doctype/quality_action/test_quality_action.py
@@ -5,42 +5,7 @@
import frappe
import unittest
-from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_unit
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal
-from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review
class TestQualityAction(unittest.TestCase):
-
- def test_quality_action(self):
- create_procedure()
- create_unit()
- create_goal()
- create_review()
- test_create_action = create_action()
- test_get_action = get_action()
-
- self.assertEquals(test_create_action, test_get_action)
-
-def create_action():
- review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
- action = frappe.get_doc({
- "doctype": "Quality Action",
- "action": "Corrective",
- "document_type": "Quality Review",
- "document_name": review,
- "date": frappe.utils.nowdate(),
- "goal": "GOAL-_Test Quality Goal",
- "procedure": "PRC-_Test Quality Procedure"
- })
- action_exist = frappe.db.exists("Quality Action", {"review": review})
-
- if not action_exist:
- action.insert()
- return action.name
- else:
- return action_exist
-
-def get_action():
- review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
- return frappe.db.exists("Quality Action", {"document_name": review})
\ No newline at end of file
+ # quality action has no code
+ pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
index a4e6aed..993274b 100644
--- a/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
+++ b/erpnext/quality_management/doctype/quality_action_resolution/quality_action_resolution.json
@@ -1,33 +1,54 @@
{
+ "actions": [],
"creation": "2019-05-26 20:36:44.337186",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"problem",
- "sb_00",
- "resolution"
+ "resolution",
+ "status",
+ "responsible",
+ "completion_by"
],
"fields": [
{
"fieldname": "problem",
"fieldtype": "Long Text",
"in_list_view": 1,
- "label": "Review"
- },
- {
- "fieldname": "sb_00",
- "fieldtype": "Section Break"
+ "label": "Problem"
},
{
"fieldname": "resolution",
"fieldtype": "Text Editor",
"in_list_view": 1,
"label": "Resolution"
+ },
+ {
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Open\nCompleted"
+ },
+ {
+ "fieldname": "responsible",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Responsible",
+ "options": "User"
+ },
+ {
+ "fieldname": "completion_by",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Completion By"
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
- "modified": "2019-05-28 13:09:50.435323",
+ "links": [],
+ "modified": "2020-10-21 12:59:25.566682",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Action Resolution",
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js
index dac6ac4..6fb3267 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js
@@ -2,31 +2,9 @@
// For license information, please see license.txt
frappe.ui.form.on('Quality Feedback', {
- refresh: function(frm) {
- frm.set_value("date", frappe.datetime.get_today());
- },
-
template: function(frm) {
if (frm.doc.template) {
- frappe.call({
- "method": "frappe.client.get",
- args: {
- doctype: "Quality Feedback Template",
- name: frm.doc.template
- },
- callback: function(data) {
- if (data && data.message) {
- frm.fields_dict.parameters.grid.remove_all();
-
- // fetch parameters from template and autofill
- for (let template_parameter of data.message.parameters) {
- let row = frm.add_child("parameters");
- row.parameter = template_parameter.parameter;
- }
- frm.refresh();
- }
- }
- });
+ frm.call('set_parameters');
}
}
});
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
index ab9084f..f3bd0dd 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.json
@@ -1,16 +1,15 @@
{
"actions": [],
- "autoname": "format:FDBK-{#####}",
+ "autoname": "format:QA-FB-{#####}",
"creation": "2019-05-26 21:23:05.308379",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "document_type",
"template",
"cb_00",
+ "document_type",
"document_name",
- "date",
"sb_00",
"parameters"
],
@@ -18,6 +17,7 @@
{
"fieldname": "template",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Template",
"options": "Quality Feedback Template",
"reqd": 1
@@ -27,13 +27,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Date",
- "read_only": 1
- },
- {
"fieldname": "sb_00",
"fieldtype": "Section Break"
},
@@ -47,6 +40,7 @@
{
"fieldname": "document_type",
"fieldtype": "Select",
+ "in_list_view": 1,
"label": "Type",
"options": "User\nCustomer",
"reqd": 1
@@ -54,13 +48,20 @@
{
"fieldname": "document_name",
"fieldtype": "Dynamic Link",
+ "in_list_view": 1,
"label": "Feedback By",
"options": "document_type",
"reqd": 1
}
],
- "links": [],
- "modified": "2020-07-03 15:50:58.589302",
+ "links": [
+ {
+ "group": "Actions",
+ "link_doctype": "Quality Action",
+ "link_fieldname": "feedback"
+ }
+ ],
+ "modified": "2020-10-27 16:20:10.918544",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Feedback",
diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
index 9894181..bf82cc0 100644
--- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.py
@@ -7,4 +7,17 @@
from frappe.model.document import Document
class QualityFeedback(Document):
- pass
\ No newline at end of file
+ def set_parameters(self):
+ if self.template and not getattr(self, 'parameters', []):
+ for d in frappe.get_doc('Quality Feedback Template', self.template).parameters:
+ self.append('parameters', dict(
+ parameter = d.parameter,
+ rating = 1
+ ))
+
+ def validate(self):
+ if not self.document_name:
+ self.document_type ='User'
+ self.document_name = frappe.session.user
+ self.set_parameters()
+
diff --git a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
index 3be1eb2..5a8bd5c 100644
--- a/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
+++ b/erpnext/quality_management/doctype/quality_feedback/test_quality_feedback.py
@@ -5,49 +5,27 @@
import frappe
import unittest
-from erpnext.quality_management.doctype.quality_feedback_template.test_quality_feedback_template import create_template
+
class TestQualityFeedback(unittest.TestCase):
-
def test_quality_feedback(self):
- create_template()
- test_create_feedback = create_feedback()
- test_get_feedback = get_feedback()
+ template = frappe.get_doc(dict(
+ doctype = 'Quality Feedback Template',
+ template = 'Test Template',
+ parameters = [
+ dict(parameter='Test Parameter 1'),
+ dict(parameter='Test Parameter 2')
+ ]
+ )).insert()
- self.assertEqual(test_create_feedback, test_get_feedback)
+ feedback = frappe.get_doc(dict(
+ doctype = 'Quality Feedback',
+ template = template.name,
+ document_type = 'User',
+ document_name = frappe.session.user
+ )).insert()
-def create_feedback():
- create_customer()
+ self.assertEqual(template.parameters[0].parameter, feedback.parameters[0].parameter)
- feedabck = frappe.get_doc({
- "doctype": "Quality Feedback",
- "template": "TMPL-_Test Feedback Template",
- "document_type": "Customer",
- "document_name": "Quality Feedback Customer",
- "date": frappe.utils.nowdate(),
- "parameters": [
- {
- "parameter": "Test Parameter",
- "rating": 3,
- "feedback": "Test Feedback"
- }
- ]
- })
-
- feedback_exists = frappe.db.exists("Quality Feedback", {"template": "TMPL-_Test Feedback Template"})
-
- if not feedback_exists:
- feedabck.insert()
- return feedabck.name
- else:
- return feedback_exists
-
-def get_feedback():
- return frappe.db.exists("Quality Feedback", {"template": "TMPL-_Test Feedback Template"})
-
-def create_customer():
- if not frappe.db.exists("Customer", {"customer_name": "Quality Feedback Customer"}):
- customer = frappe.get_doc({
- "doctype": "Customer",
- "customer_name": "Quality Feedback Customer"
- }).insert(ignore_permissions=True)
\ No newline at end of file
+ feedback.delete()
+ template.delete()
diff --git a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
index 5bd8920..ce5d4cf 100644
--- a/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
+++ b/erpnext/quality_management/doctype/quality_feedback_parameter/quality_feedback_parameter.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-05-26 21:25:01.715807",
"doctype": "DocType",
"editable_grid": 1,
@@ -39,12 +40,13 @@
"fieldname": "feedback",
"fieldtype": "Text Editor",
"in_list_view": 1,
- "label": "Feedback",
- "reqd": 1
+ "label": "Feedback"
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
- "modified": "2019-07-13 19:58:08.966141",
+ "links": [],
+ "modified": "2020-10-27 17:28:12.033145",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Feedback Parameter",
diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
index bdc9dba..1696470 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
+++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
@@ -1,13 +1,12 @@
{
"actions": [],
- "autoname": "format:TMPL-{template}",
+ "autoname": "field:template",
"creation": "2019-05-26 21:17:24.283061",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"template",
- "cb_00",
"sb_00",
"parameters"
],
@@ -16,12 +15,9 @@
"fieldname": "template",
"fieldtype": "Data",
"in_list_view": 1,
- "label": "Template",
- "reqd": 1
- },
- {
- "fieldname": "cb_00",
- "fieldtype": "Column Break"
+ "label": "Template Name",
+ "reqd": 1,
+ "unique": 1
},
{
"fieldname": "sb_00",
@@ -35,8 +31,14 @@
"reqd": 1
}
],
- "links": [],
- "modified": "2020-07-03 16:06:03.749415",
+ "links": [
+ {
+ "group": "Records",
+ "link_doctype": "Quality Feedback",
+ "link_fieldname": "template"
+ }
+ ],
+ "modified": "2020-10-27 16:18:53.579688",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Feedback Template",
diff --git a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
index 36dbe13..b3eed10 100644
--- a/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
+++ b/erpnext/quality_management/doctype/quality_feedback_template/test_quality_feedback_template.py
@@ -7,31 +7,4 @@
import unittest
class TestQualityFeedbackTemplate(unittest.TestCase):
-
- def test_quality_feedback_template(self):
- test_create_template = create_template()
- test_get_template = get_template()
-
- self.assertEqual(test_create_template, test_get_template)
-
-def create_template():
- template = frappe.get_doc({
- "doctype": "Quality Feedback Template",
- "template": "_Test Feedback Template",
- "parameters": [
- {
- "parameter": "Test Parameter"
- }
- ]
- })
-
- template_exists = frappe.db.exists("Quality Feedback Template", {"template": "_Test Feedback Template"})
-
- if not template_exists:
- template.insert()
- return template.name
- else:
- return template_exists
-
-def get_template():
- return frappe.db.exists("Quality Feedback Template", {"template": "_Test Feedback Template"})
\ No newline at end of file
+ pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.js b/erpnext/quality_management/doctype/quality_goal/quality_goal.js
index ff58c5a..40cb4d9 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.js
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.js
@@ -2,7 +2,6 @@
// For license information, please see license.txt
frappe.ui.form.on('Quality Goal', {
- refresh: function(frm) {
- frm.doc.created_by = frappe.session.user;
- }
+ // refresh: function(frm) {
+ // }
});
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.json b/erpnext/quality_management/doctype/quality_goal/quality_goal.json
index c326109..2680255 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.json
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.json
@@ -1,5 +1,6 @@
{
- "autoname": "format:GOAL-{goal}",
+ "actions": [],
+ "autoname": "field:goal",
"creation": "2018-10-02 12:17:41.727541",
"doctype": "DocType",
"editable_grid": 1,
@@ -7,28 +8,15 @@
"field_order": [
"goal",
"frequency",
- "created_by",
"cb_00",
"procedure",
"weekday",
- "quarter",
"date",
- "sb_00",
- "revision",
- "cb_01",
- "revised_on",
"sb_01",
"objectives"
],
"fields": [
{
- "fieldname": "created_by",
- "fieldtype": "Link",
- "label": "Created By",
- "options": "User",
- "read_only": 1
- },
- {
"default": "None",
"fieldname": "frequency",
"fieldtype": "Select",
@@ -51,20 +39,6 @@
"options": "1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20\n21\n22\n23\n24\n25\n26\n27\n28\n29\n30"
},
{
- "default": "0",
- "fieldname": "revision",
- "fieldtype": "Int",
- "label": "Revision",
- "read_only": 1
- },
- {
- "fieldname": "revised_on",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Revised On",
- "read_only": 1
- },
- {
"depends_on": "eval:doc.frequency == 'Weekly';",
"fieldname": "weekday",
"fieldtype": "Select",
@@ -76,15 +50,6 @@
"fieldtype": "Column Break"
},
{
- "fieldname": "sb_00",
- "fieldtype": "Section Break",
- "label": "Revision and Revised On"
- },
- {
- "fieldname": "cb_01",
- "fieldtype": "Column Break"
- },
- {
"fieldname": "sb_01",
"fieldtype": "Section Break",
"label": "Objectives"
@@ -101,18 +66,17 @@
"label": "Goal",
"reqd": 1,
"unique": 1
- },
- {
- "default": "January-April-July-October",
- "depends_on": "eval:doc.frequency == 'Quarterly';",
- "fieldname": "quarter",
- "fieldtype": "Select",
- "label": "Quarter",
- "options": "January-April-July-October",
- "read_only": 1
}
],
- "modified": "2019-05-28 14:49:12.768863",
+ "index_web_pages_for_search": 1,
+ "links": [
+ {
+ "group": "Review",
+ "link_doctype": "Quality Review",
+ "link_fieldname": "goal"
+ }
+ ],
+ "modified": "2020-10-27 15:57:59.368605",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Goal",
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.py b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
index 4ae015e..f3fe986 100644
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.py
@@ -8,7 +8,5 @@
from frappe.model.document import Document
class QualityGoal(Document):
-
def validate(self):
- self.revision += 1
- self.revised_on = frappe.utils.today()
+ pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py b/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py
deleted file mode 100644
index 22af3c0..0000000
--- a/erpnext/quality_management/doctype/quality_goal/quality_goal_dashboard.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'goal',
- 'transactions': [
- {
- 'label': _('Review'),
- 'items': ['Quality Review']
- }
- ]
- }
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js
deleted file mode 100644
index f8afe54..0000000
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Goal", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quality Goal
- () => frappe.tests.make('Quality Goal', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
index d77187a..f61d6e5 100644
--- a/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
+++ b/erpnext/quality_management/doctype/quality_goal/test_quality_goal.py
@@ -8,44 +8,18 @@
from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
class TestQualityGoal(unittest.TestCase):
-
def test_quality_goal(self):
- create_procedure()
- create_unit()
- test_create_goal = create_goal()
- test_get_goal = get_goal()
+ # no code, just a basic sanity check
+ goal = get_quality_goal()
+ self.assertTrue(goal)
+ goal.delete()
- self.assertEquals(test_create_goal, test_get_goal)
-
-def create_goal():
- goal = frappe.get_doc({
- "doctype": "Quality Goal",
- "goal": "_Test Quality Goal",
- "procedure": "PRC-_Test Quality Procedure",
- "objectives": [
- {
- "objective": "_Test Quality Objective",
- "target": "4",
- "uom": "_Test UOM"
- }
+def get_quality_goal():
+ return frappe.get_doc(dict(
+ doctype = 'Quality Goal',
+ goal = 'Test Quality Module',
+ frequency = 'Daily',
+ objectives = [
+ dict(objective = 'Check test cases', target='100', uom='Percent')
]
- })
- goal_exist = frappe.db.exists("Quality Goal", {"goal": goal.goal})
- if not goal_exist:
- goal.insert()
- return goal.name
- else:
- return goal_exist
-
-def get_goal():
- goal = frappe.db.exists("Quality Goal", "GOAL-_Test Quality Goal")
- return goal
-
-def create_unit():
- unit = frappe.get_doc({
- "doctype": "UOM",
- "uom_name": "_Test UOM",
- })
- unit_exist = frappe.db.exists("UOM", unit.uom_name)
- if not unit_exist:
- unit.insert()
+ )).insert()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js
index 32c7c33..eb7a8c3 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js
@@ -2,8 +2,5 @@
// For license information, please see license.txt
frappe.ui.form.on('Quality Meeting', {
- onload: function(frm){
- frm.set_value("date", frappe.datetime.get_today());
- frm.refresh();
- }
+
});
diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
index 7691fe3..ead403d 100644
--- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
+++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.json
@@ -1,15 +1,13 @@
{
"actions": [],
- "autoname": "naming_series:",
+ "autoname": "format:QA-MEET-{YY}-{MM}-{DD}",
"creation": "2018-10-15 16:25:41.548432",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "naming_series",
- "date",
- "cb_00",
"status",
+ "cb_00",
"sb_00",
"agenda",
"sb_01",
@@ -17,13 +15,6 @@
],
"fields": [
{
- "fieldname": "date",
- "fieldtype": "Date",
- "in_list_view": 1,
- "label": "Date",
- "read_only": 1
- },
- {
"default": "Open",
"fieldname": "status",
"fieldtype": "Select",
@@ -55,16 +46,11 @@
"fieldname": "sb_01",
"fieldtype": "Section Break",
"label": "Minutes"
- },
- {
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "label": "Naming Series",
- "options": "MTNG-.YYYY.-.MM.-.DD.-"
}
],
+ "index_web_pages_for_search": 1,
"links": [],
- "modified": "2020-05-19 13:18:59.821740",
+ "modified": "2020-10-27 16:36:45.657883",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Meeting",
diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js
deleted file mode 100644
index 196cc85..0000000
--- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Meeting", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quality Meeting
- () => frappe.tests.make('Quality Meeting', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
index e61b5df..754bccb 100644
--- a/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
+++ b/erpnext/quality_management/doctype/quality_meeting/test_quality_meeting.py
@@ -5,41 +5,7 @@
import frappe
import unittest
-from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review
class TestQualityMeeting(unittest.TestCase):
- def test_quality_meeting(self):
- create_review()
- test_create_meeting = create_meeting()
- test_get_meeting = get_meeting()
- self.assertEquals(test_create_meeting, test_get_meeting)
-
-def create_meeting():
- meeting = frappe.get_doc({
- "doctype": "Quality Meeting",
- "status": "Open",
- "date": frappe.utils.nowdate(),
- "agenda": [
- {
- "agenda": "Test Agenda"
- }
- ],
- "minutes": [
- {
- "document_type": "Quality Review",
- "document_name": frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"}),
- "minute": "Test Minute"
- }
- ]
- })
- meeting_exist = frappe.db.exists("Quality Meeting", {"date": frappe.utils.nowdate(), "status": "Open"})
-
- if not meeting_exist:
- meeting.insert()
- return meeting.name
- else:
- return meeting_exist
-
-def get_meeting():
- meeting = frappe.db.exists("Quality Meeting", {"date": frappe.utils.nowdate(), "status": "Open"})
- return meeting
\ No newline at end of file
+ # nothing to test
+ pass
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
index 1ed921c..f588f9a 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
@@ -1,19 +1,22 @@
{
"actions": [],
"allow_rename": 1,
- "autoname": "format:PRC-{quality_procedure_name}",
+ "autoname": "field:quality_procedure_name",
"creation": "2018-10-06 00:06:29.756804",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"quality_procedure_name",
+ "process_owner",
+ "process_owner_full_name",
+ "section_break_3",
+ "processes",
+ "sb_00",
"parent_quality_procedure",
"is_group",
- "sb_00",
- "processes",
- "lft",
"rgt",
+ "lft",
"old_parent"
],
"fields": [
@@ -34,14 +37,14 @@
"fieldname": "lft",
"fieldtype": "Int",
"hidden": 1,
- "label": "Lft",
+ "label": "Left Index",
"read_only": 1
},
{
"fieldname": "rgt",
"fieldtype": "Int",
"hidden": 1,
- "label": "Rgt",
+ "label": "Right Index",
"read_only": 1
},
{
@@ -54,7 +57,7 @@
{
"fieldname": "sb_00",
"fieldtype": "Section Break",
- "label": "Processes"
+ "label": "Parent"
},
{
"fieldname": "processes",
@@ -67,12 +70,52 @@
"fieldtype": "Data",
"in_list_view": 1,
"label": "Quality Procedure",
- "reqd": 1
+ "reqd": 1,
+ "unique": 1
+ },
+ {
+ "fieldname": "process_owner",
+ "fieldtype": "Link",
+ "label": "Process Owner",
+ "options": "User"
+ },
+ {
+ "fieldname": "section_break_3",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fetch_from": "process_owner.full_name",
+ "fieldname": "process_owner_full_name",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Process Owner Full Name",
+ "print_hide": 1
}
],
"is_tree": 1,
- "links": [],
- "modified": "2020-10-13 11:46:07.744194",
+ "links": [
+ {
+ "group": "Reviews",
+ "link_doctype": "Quality Review",
+ "link_fieldname": "procedure"
+ },
+ {
+ "group": "Goals",
+ "link_doctype": "Quality Goal",
+ "link_fieldname": "procedure"
+ },
+ {
+ "group": "Actions",
+ "link_doctype": "Quality Action",
+ "link_fieldname": "procedure"
+ },
+ {
+ "group": "Actions",
+ "link_doctype": "Non Conformance",
+ "link_fieldname": "procedure"
+ }
+ ],
+ "modified": "2020-10-26 15:25:39.316088",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Procedure",
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index 797c26b..53f4e6c 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -14,69 +14,58 @@
self.check_for_incorrect_child()
def on_update(self):
+ NestedSet.on_update(self)
self.set_parent()
def after_insert(self):
self.set_parent()
- #if Child is Added through Tree View.
+
+ # add child to parent if missing
if self.parent_quality_procedure:
- parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
- parent_quality_procedure.append("processes", {"procedure": self.name})
- parent_quality_procedure.save()
+ parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
+ if not [d for d in parent.processes if d.procedure == self.name]:
+ parent.append("processes", {"procedure": self.name, "process_description": self.name})
+ parent.save()
def on_trash(self):
- if self.parent_quality_procedure:
- doc = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
- for process in doc.processes:
- if process.procedure == self.name:
- doc.processes.remove(process)
- doc.save(ignore_permissions=True)
-
- flag_is_group = 0
- doc.load_from_db()
-
- for process in doc.processes:
- flag_is_group = 1 if process.procedure else 0
-
- doc.is_group = 0 if flag_is_group == 0 else 1
- doc.save(ignore_permissions=True)
+ # clear from child table (sub procedures)
+ frappe.db.sql('''update `tabQuality Procedure Process`
+ set `procedure`='' where `procedure`=%s''', self.name)
+ NestedSet.on_trash(self, allow_root_deletion=True)
def set_parent(self):
- rebuild_tree('Quality Procedure', 'parent_quality_procedure')
-
for process in self.processes:
# Set parent for only those children who don't have a parent
- parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
- if not parent_quality_procedure and process.procedure:
+ has_parent = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
+ if not has_parent and process.procedure:
frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name)
def check_for_incorrect_child(self):
for process in self.processes:
if process.procedure:
+ self.is_group = 1
# Check if any child process belongs to another parent.
parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
if parent_quality_procedure and parent_quality_procedure != self.name:
- frappe.throw(_("{0} already has a Parent Procedure {1}.".format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure))),
+ frappe.throw(_("{0} already has a Parent Procedure {1}.").format(frappe.bold(process.procedure), frappe.bold(parent_quality_procedure)),
title=_("Invalid Child Procedure"))
- self.is_group = 1
@frappe.whitelist()
def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
if parent is None or parent == "All Quality Procedures":
parent = ""
- return frappe.db.sql("""
- select
- name as value,
- is_group as expandable
- from
- `tab{doctype}`
- where
- ifnull(parent_quality_procedure, "")={parent}
- """.format(
- doctype = doctype,
- parent=frappe.db.escape(parent)
- ), as_dict=1)
+ if parent:
+ parent_procedure = frappe.get_doc('Quality Procedure', parent)
+ # return the list in order
+ return [dict(
+ value=d.procedure,
+ expandable=frappe.db.get_value('Quality Procedure', d.procedure, 'is_group'))
+ for d in parent_procedure.processes if d.procedure
+ ]
+ else:
+ return frappe.get_all(doctype, fields=['name as value', 'is_group as expandable'],
+ filters = dict(parent_quality_procedure = parent), order_by='name asc')
@frappe.whitelist()
def add_node():
@@ -88,4 +77,4 @@
if args.parent_quality_procedure == 'All Quality Procedures':
args.parent_quality_procedure = None
- frappe.get_doc(args).insert()
\ No newline at end of file
+ return frappe.get_doc(args).insert()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py
deleted file mode 100644
index 407028b..0000000
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_dashboard.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from frappe import _
-
-def get_data():
- return {
- 'fieldname': 'procedure',
- 'transactions': [
- {
- 'label': _('Goal'),
- 'items': ['Quality Goal']
- },
- {
- 'label': _('Review'),
- 'items': ['Quality Review']
- },
- {
- 'label': _('Action'),
- 'items': ['Quality Action']
- }
- ],
- }
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
index ef48ab6..eeb4cf6 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js
@@ -15,7 +15,7 @@
}
},
],
- breadcrumb: "Setup",
+ breadcrumb: "Quality Management",
disable_add_node: true,
root_label: "All Quality Procedures",
get_tree_root: false,
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js
deleted file mode 100644
index 0a187eb..0000000
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Quality Procedure", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Quality Procedure
- () => frappe.tests.make('Quality Procedure', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
index 3289bb5..36bdf26 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -6,54 +6,45 @@
import frappe
import unittest
-class TestQualityProcedure(unittest.TestCase):
- def test_quality_procedure(self):
- test_create_procedure = create_procedure()
- test_create_nested_procedure = create_nested_procedure()
- test_get_procedure, test_get_nested_procedure = get_procedure()
+from .quality_procedure import add_node
- self.assertEquals(test_create_procedure, test_get_procedure.get("name"))
- self.assertEquals(test_create_nested_procedure, test_get_nested_procedure.get("name"))
+class TestQualityProcedure(unittest.TestCase):
+ def test_add_node(self):
+ try:
+ procedure = frappe.get_doc(dict(
+ doctype = 'Quality Procedure',
+ quality_procedure_name = 'Test Procedure 1',
+ processes = [
+ dict(process_description = 'Test Step 1')
+ ]
+ )).insert()
+
+ frappe.form_dict = dict(doctype = 'Quality Procedure', quality_procedure_name = 'Test Child 1',
+ parent_quality_procedure = procedure.name, cmd='test', is_root='false')
+ node = add_node()
+
+ procedure.reload()
+
+ self.assertEqual(procedure.is_group, 1)
+
+ # child row created
+ self.assertTrue([d for d in procedure.processes if d.procedure == node.name])
+
+ node.delete()
+ procedure.reload()
+
+ # child unset
+ self.assertFalse([d for d in procedure.processes if d.name == node.name])
+
+ finally:
+ procedure.delete()
def create_procedure():
- procedure = frappe.get_doc({
- "doctype": "Quality Procedure",
- "quality_procedure_name": "_Test Quality Procedure",
- "processes": [
- {
- "process_description": "_Test Quality Procedure Table",
- }
+ return frappe.get_doc(dict(
+ doctype = 'Quality Procedure',
+ quality_procedure_name = 'Test Procedure 1',
+ is_group = 1,
+ processes = [
+ dict(process_description = 'Test Step 1')
]
- })
-
- procedure_exist = frappe.db.exists("Quality Procedure", "PRC-_Test Quality Procedure")
-
- if not procedure_exist:
- procedure.insert()
- return procedure.name
- else:
- return procedure_exist
-
-def create_nested_procedure():
- nested_procedure = frappe.get_doc({
- "doctype": "Quality Procedure",
- "quality_procedure_name": "_Test Nested Quality Procedure",
- "processes": [
- {
- "procedure": "PRC-_Test Quality Procedure"
- }
- ]
- })
-
- nested_procedure_exist = frappe.db.exists("Quality Procedure", "PRC-_Test Nested Quality Procedure")
-
- if not nested_procedure_exist:
- nested_procedure.insert()
- return nested_procedure.name
- else:
- return nested_procedure_exist
-
-def get_procedure():
- procedure = frappe.get_doc("Quality Procedure", "PRC-_Test Quality Procedure")
- nested_procedure = frappe.get_doc("Quality Procedure", "PRC-_Test Nested Quality Procedure")
- return {"name": procedure.name}, {"name": nested_procedure.name, "parent_quality_procedure": nested_procedure.parent_quality_procedure}
\ No newline at end of file
+ )).insert()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
index 3925dbb..aeca6ff 100644
--- a/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
+++ b/erpnext/quality_management/doctype/quality_procedure_process/quality_procedure_process.json
@@ -10,6 +10,7 @@
],
"fields": [
{
+ "columns": 8,
"fieldname": "process_description",
"fieldtype": "Text Editor",
"in_list_view": 1,
@@ -20,13 +21,14 @@
"fieldname": "procedure",
"fieldtype": "Link",
"in_list_view": 1,
- "label": "Child Procedure",
+ "label": "Sub Procedure",
"options": "Quality Procedure"
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-06-17 15:44:38.937915",
+ "modified": "2020-10-27 13:55:11.252945",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Procedure Process",
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.js b/erpnext/quality_management/doctype/quality_review/quality_review.js
index b624581..67371bf 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.js
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.js
@@ -2,9 +2,6 @@
// For license information, please see license.txt
frappe.ui.form.on('Quality Review', {
- onload: function(frm){
- frm.set_value("date", frappe.datetime.get_today());
- },
goal: function(frm) {
frappe.call({
"method": "frappe.client.get",
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.json b/erpnext/quality_management/doctype/quality_review/quality_review.json
index 76714ce..31ad341 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.json
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.json
@@ -1,6 +1,6 @@
{
"actions": [],
- "autoname": "format:REV-{#####}",
+ "autoname": "format:QA-REV-{#####}",
"creation": "2018-10-02 11:45:16.301955",
"doctype": "DocType",
"editable_grid": 1,
@@ -18,6 +18,7 @@
],
"fields": [
{
+ "default": "Today",
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
@@ -50,7 +51,7 @@
"collapsible": 1,
"fieldname": "sb_01",
"fieldtype": "Section Break",
- "label": "Additional Information"
+ "label": "Notes"
},
{
"fieldname": "reviews",
@@ -63,7 +64,8 @@
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
- "options": "Open\nClosed"
+ "options": "Open\nPassed\nFailed",
+ "read_only": 1
},
{
"fieldname": "goal",
@@ -74,8 +76,15 @@
"reqd": 1
}
],
- "links": [],
- "modified": "2020-02-01 10:59:38.933115",
+ "index_web_pages_for_search": 1,
+ "links": [
+ {
+ "group": "Review",
+ "link_doctype": "Quality Action",
+ "link_fieldname": "review"
+ }
+ ],
+ "modified": "2020-10-21 12:56:47.046172",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Review",
@@ -120,5 +129,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "title_field": "goal",
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.py b/erpnext/quality_management/doctype/quality_review/quality_review.py
index 2bc8867..e3a8b07 100644
--- a/erpnext/quality_management/doctype/quality_review/quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/quality_review.py
@@ -7,7 +7,26 @@
from frappe.model.document import Document
class QualityReview(Document):
- pass
+ def validate(self):
+ # fetch targets from goal
+ if not self.reviews:
+ for d in frappe.get_doc('Quality Goal', self.goal).objectives:
+ self.append('reviews', dict(
+ objective = d.objective,
+ target = d.target,
+ uom = d.uom
+ ))
+
+ self.set_status()
+
+ def set_status(self):
+ # if any child item is failed, fail the parent
+ if not len(self.reviews or []) or any([d.status=='Open' for d in self.reviews]):
+ self.status = 'Open'
+ elif any([d.status=='Failed' for d in self.reviews]):
+ self.status = 'Failed'
+ else:
+ self.status = 'Passed'
def review():
day = frappe.utils.getdate().day
@@ -24,7 +43,7 @@
elif goal.frequency == 'Monthly' and goal.date == str(day):
create_review(goal.name)
- elif goal.frequency == 'Quarterly' and goal.data == str(day) and get_quarter(month):
+ elif goal.frequency == 'Quarterly' and day==1 and get_quarter(month):
create_review(goal.name)
def create_review(goal):
@@ -36,15 +55,6 @@
"date": frappe.utils.getdate()
})
- for objective in goal.objectives:
- review.append("reviews",
- {
- "objective": objective.objective,
- "target": objective.target,
- "uom": objective.uom
- }
- )
-
review.insert(ignore_permissions=True)
def get_quarter(month):
diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.js b/erpnext/quality_management/doctype/quality_review/test_quality_review.js
deleted file mode 100644
index cf910b2..0000000
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* eslint-disable */
-// rename this file from _test_[name] to test_[name] to activate
-// and remove above this line
-
-QUnit.test("test: Performance Monitoring", function (assert) {
- let done = assert.async();
-
- // number of asserts
- assert.expect(1);
-
- frappe.run_serially([
- // insert a new Performance Monitoring
- () => frappe.tests.make('Performance Monitoring', [
- // values to be set
- {key: 'value'}
- ]),
- () => {
- assert.equal(cur_frm.doc.key, 'value');
- },
- () => done()
- ]);
-
-});
diff --git a/erpnext/quality_management/doctype/quality_review/test_quality_review.py b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
index 8add6db..a7d92da 100644
--- a/erpnext/quality_management/doctype/quality_review/test_quality_review.py
+++ b/erpnext/quality_management/doctype/quality_review/test_quality_review.py
@@ -5,42 +5,18 @@
import frappe
import unittest
-from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_unit
-from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal
+
+from ..quality_goal.test_quality_goal import get_quality_goal
+from .quality_review import review
class TestQualityReview(unittest.TestCase):
+ def test_review_creation(self):
+ quality_goal = get_quality_goal()
+ review()
- def test_quality_review(self):
- create_procedure()
- create_unit()
- create_goal()
- test_create_review = create_review()
- test_get_review = get_review()
- self.assertEquals(test_create_review, test_get_review)
+ # check if review exists
+ quality_review = frappe.get_doc('Quality Review', dict(goal = quality_goal.name))
+ self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target)
+ quality_review.delete()
-def create_review():
- review = frappe.get_doc({
- "doctype": "Quality Review",
- "goal": "GOAL-_Test Quality Goal",
- "procedure": "PRC-_Test Quality Procedure",
- "date": frappe.utils.nowdate(),
- "reviews": [
- {
- "objective": "_Test Quality Objective",
- "target": "100",
- "uom": "_Test UOM",
- "review": "Test Review"
- }
- ]
- })
- review_exist = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
- if not review_exist:
- review.insert(ignore_permissions=True)
- return review.name
- else:
- return review_exist
-
-def get_review():
- review = frappe.db.exists("Quality Review", {"goal": "GOAL-_Test Quality Goal"})
- return review
\ No newline at end of file
+ quality_goal.delete()
\ No newline at end of file
diff --git a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
index 91f7bc0..3a750c2 100644
--- a/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
+++ b/erpnext/quality_management/doctype/quality_review_objective/quality_review_objective.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-05-26 15:17:44.796958",
"doctype": "DocType",
"editable_grid": 1,
@@ -9,10 +10,12 @@
"target",
"uom",
"sb_00",
+ "status",
"review"
],
"fields": [
{
+ "columns": 3,
"fieldname": "objective",
"fieldtype": "Text",
"in_list_view": 1,
@@ -20,6 +23,7 @@
"read_only": 1
},
{
+ "columns": 2,
"fieldname": "target",
"fieldtype": "Data",
"in_list_view": 1,
@@ -27,6 +31,7 @@
"read_only": 1
},
{
+ "columns": 1,
"fetch_from": "target_unit",
"fieldname": "uom",
"fieldtype": "Link",
@@ -49,10 +54,20 @@
{
"fieldname": "cb_00",
"fieldtype": "Column Break"
+ },
+ {
+ "columns": 2,
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Open\nPassed\nFailed"
}
],
+ "index_web_pages_for_search": 1,
"istable": 1,
- "modified": "2019-05-26 16:14:12.586128",
+ "links": [],
+ "modified": "2020-10-27 16:28:20.908637",
"modified_by": "Administrator",
"module": "Quality Management",
"name": "Quality Review Objective",
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index e5b50d7..6b9939e 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -14,11 +14,11 @@
def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
data = dict()
result = []
- warehouse, show_only_available_items = "", False
+ warehouse, hide_unavailable_items = "", False
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
if not allow_negative_stock:
- warehouse, show_only_available_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'show_only_available_items'])
+ warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
if not frappe.db.exists('Item Group', item_group):
item_group = get_root_of('Item Group')
@@ -48,7 +48,7 @@
lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
bin_join_selection, bin_join_condition = "", ""
- if show_only_available_items:
+ if hide_unavailable_items:
bin_join_selection = ", `tabBin` bin"
bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index 3643174..dd95075 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -236,7 +236,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-09-12 16:11:31.910508",
+ "modified": "2020-10-21 13:03:11.938072",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection",
@@ -257,7 +257,6 @@
"write": 1
}
],
- "quick_entry": 1,
"search_fields": "item_code, report_date, reference_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 4583c51..7685267 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -615,6 +615,15 @@
if not row.subcontracted_item:
frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
.format(row.idx, frappe.bold(row.item_code)))
+ elif not row.po_detail:
+ filters = {
+ "parent": self.purchase_order, "docstatus": 1,
+ "rm_item_code": row.item_code, "main_item_code": row.subcontracted_item
+ }
+
+ po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name")
+ if po_detail:
+ row.db_set("po_detail", po_detail)
def validate_bom(self):
for d in self.get('items'):
diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py
index 042087a..1339d9b 100644
--- a/erpnext/stock/report/stock_balance/stock_balance.py
+++ b/erpnext/stock/report/stock_balance/stock_balance.py
@@ -168,6 +168,7 @@
from
`tabStock Ledger Entry` sle force index (posting_sort_index)
where sle.docstatus < 2 %s %s
+ and is_cancelled = 0
order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec
(item_conditions_sql, conditions), as_dict=1)
diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py
index 920c13c..62b39cc 100644
--- a/erpnext/support/doctype/issue/issue.py
+++ b/erpnext/support/doctype/issue/issue.py
@@ -7,7 +7,7 @@
from frappe import _
from frappe import utils
from frappe.model.document import Document
-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 frappe.utils import now_datetime, getdate, get_weekdays, add_to_date, get_time, get_datetime, time_diff_in_seconds
from datetime import datetime, timedelta
from frappe.model.mapper import get_mapped_doc
from frappe.utils.user import is_website_user
@@ -355,13 +355,13 @@
doc = frappe.get_doc("Issue", issue.name)
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer
- variance = round(time_diff_in_hours(doc.response_by, current_time), 2)
+ variance = round(time_diff_in_seconds(doc.response_by, current_time), 2)
frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
if variance < 0:
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
if not doc.resolution_date: # resolution_date set when issue has been closed
- variance = round(time_diff_in_hours(doc.resolution_by, current_time), 2)
+ variance = round(time_diff_in_seconds(doc.resolution_by, current_time), 2)
frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
if variance < 0:
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)
diff --git a/erpnext/www/lms/index.html b/erpnext/www/lms/index.html
index 7ce3521..7b239ac 100644
--- a/erpnext/www/lms/index.html
+++ b/erpnext/www/lms/index.html
@@ -45,7 +45,7 @@
<p class='lead'>{{ education_settings.description }}</p>
<p class="mt-4">
{% if frappe.session.user == 'Guest' %}
- <a class="btn btn-primary btn-lg" href="'/login#signup'">{{_('Sign Up')}}</a>
+ <a class="btn btn-primary btn-lg" href="/login#signup">{{_('Sign Up')}}</a>
{% endif %}
</p>
</div>
@@ -62,4 +62,4 @@
</div>
</div>
</section>
-{% endblock %}
\ No newline at end of file
+{% endblock %}