Merge branch 'develop' into purchase-invoice-payment-terms

This commit is contained in:
Marica
2020-11-05 12:25:38 +05:30
committed by GitHub
165 changed files with 8051 additions and 1643 deletions

View File

@@ -7,7 +7,7 @@ frappe.ui.form.on('Accounting Dimension', {
frm.set_query('document_type', () => { frm.set_query('document_type', () => {
let invalid_doctypes = frappe.model.core_doctypes_list; let invalid_doctypes = frappe.model.core_doctypes_list;
invalid_doctypes.push('Accounting Dimension', 'Project', invalid_doctypes.push('Accounting Dimension', 'Project',
'Cost Center', 'Accounting Dimension Detail'); 'Cost Center', 'Accounting Dimension Detail', 'Company');
return { return {
filters: { filters: {

View File

@@ -19,7 +19,7 @@ class AccountingDimension(Document):
def validate(self): def validate(self):
if self.document_type in core_doctypes_list + ('Accounting Dimension', 'Project', 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) msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg) frappe.throw(msg)

View File

@@ -158,8 +158,11 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero(nowdate(), "cost_center") set_total_expense_zero(nowdate(), "cost_center")
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
month = now_datetime().month
if month > 10:
month = 10
for i in range(now_datetime().month): for i in range(month):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True) "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
@@ -177,8 +180,11 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero(nowdate(), "project") set_total_expense_zero(nowdate(), "project")
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
month = now_datetime().month
if month > 10:
month = 10
for i in range(now_datetime().month): for i in range(month):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project") "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project")

View File

@@ -14,6 +14,7 @@
"column_break_9", "column_break_9",
"update_stock", "update_stock",
"ignore_pricing_rule", "ignore_pricing_rule",
"hide_unavailable_items",
"warehouse", "warehouse",
"campaign", "campaign",
"company_address", "company_address",
@@ -307,13 +308,19 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Update Stock", "label": "Update Stock",
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "hide_unavailable_items",
"fieldtype": "Check",
"label": "Hide Unavailable Items"
} }
], ],
"icon": "icon-cog", "icon": "icon-cog",
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2020-10-20 13:16:50.665081", "modified": "2020-10-29 13:18:38.795925",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Profile", "name": "POS Profile",

View File

@@ -504,10 +504,10 @@
}, },
{ {
"default": "0", "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", "fieldname": "apply_discount_on_rate",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Apply Discount on Rate" "label": "Apply Discount on Discounted Rate"
}, },
{ {
"default": "0", "default": "0",
@@ -563,7 +563,7 @@
"icon": "fa fa-gift", "icon": "fa fa-gift",
"idx": 1, "idx": 1,
"links": [], "links": [],
"modified": "2020-08-26 12:24:44.740734", "modified": "2020-10-28 16:53:14.416172",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Pricing Rule", "name": "Pricing Rule",

View File

@@ -60,6 +60,15 @@ class PricingRule(Document):
if self.price_or_product_discount == 'Price' and not self.rate_or_discount: 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) 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): def validate_applicable_for_selling_or_buying(self):
if not self.selling and not self.buying: if not self.selling and not self.buying:
throw(_("Atleast one of the Selling or Buying must be selected")) throw(_("Atleast one of the Selling or Buying must be selected"))
@@ -226,12 +235,11 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
item_details = frappe._dict({ item_details = frappe._dict({
"doctype": args.doctype, "doctype": args.doctype,
"has_margin": False,
"name": args.name, "name": args.name,
"parent": args.parent, "parent": args.parent,
"parenttype": args.parenttype, "parenttype": args.parenttype,
"child_docname": args.get('child_docname'), "child_docname": args.get('child_docname')
"discount_percentage_on_rate": [],
"discount_amount_on_rate": []
}) })
if args.ignore_pricing_rule or not args.item_code: if args.ignore_pricing_rule or not args.item_code:
@@ -279,6 +287,10 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
else: else:
get_product_discount_rule(pricing_rule, item_details, args, doc) 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.has_pricing_rule = 1
item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules]) item_details.pricing_rules = frappe.as_json([d.pricing_rule for d in rules])
@@ -330,13 +342,11 @@ def get_pricing_rule_details(args, pricing_rule):
def apply_price_discount_rule(pricing_rule, item_details, args): def apply_price_discount_rule(pricing_rule, item_details, args):
item_details.pricing_rule_for = pricing_rule.rate_or_discount 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')): or (pricing_rule.margin_type == 'Percentage')):
item_details.margin_type = pricing_rule.margin_type item_details.margin_type = pricing_rule.margin_type
item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount item_details.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
else: item_details.has_margin = True
item_details.margin_type = None
item_details.margin_rate_or_amount = 0.0
if pricing_rule.rate_or_discount == 'Rate': if pricing_rule.rate_or_discount == 'Rate':
pricing_rule_rate = 0.0 pricing_rule_rate = 0.0
@@ -351,9 +361,9 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
if pricing_rule.rate_or_discount != apply_on: continue if pricing_rule.rate_or_discount != apply_on: continue
field = frappe.scrub(apply_on) field = frappe.scrub(apply_on)
if pricing_rule.apply_discount_on_rate: if pricing_rule.apply_discount_on_rate and item_details.get("discount_percentage"):
discount_field = "{0}_on_rate".format(field) # Apply discount on discounted rate
item_details[discount_field].append(pricing_rule.get(field, 0)) item_details[field] += ((100 - item_details[field]) * (pricing_rule.get(field, 0) / 100))
else: else:
if field not in item_details: if field not in item_details:
item_details.setdefault(field, 0) item_details.setdefault(field, 0)
@@ -361,14 +371,6 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
item_details[field] += (pricing_rule.get(field, 0) item_details[field] += (pricing_rule.get(field, 0)
if pricing_rule else args.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): 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, from erpnext.accounts.doctype.pricing_rule.utils import (get_applied_pricing_rules,
get_pricing_rule_items) get_pricing_rule_items)

View File

@@ -457,6 +457,33 @@ class TestPricingRule(unittest.TestCase):
item = si.items[0] item = si.items[0]
self.assertEquals(item.rate, 900) 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): def make_pricing_rule(**args):
args = frappe._dict(args) args = frappe._dict(args)
@@ -468,6 +495,7 @@ def make_pricing_rule(**args):
"applicable_for": args.applicable_for, "applicable_for": args.applicable_for,
"selling": args.selling or 0, "selling": args.selling or 0,
"currency": "USD", "currency": "USD",
"apply_discount_on_rate": args.apply_discount_on_rate or 0,
"buying": args.buying or 0, "buying": args.buying or 0,
"min_qty": args.min_qty or 0.0, "min_qty": args.min_qty or 0.0,
"max_qty": args.max_qty or 0.0, "max_qty": args.max_qty or 0.0,
@@ -476,9 +504,13 @@ def make_pricing_rule(**args):
"rate": args.rate or 0.0, "rate": args.rate or 0.0,
"margin_type": args.margin_type, "margin_type": args.margin_type,
"margin_rate_or_amount": args.margin_rate_or_amount or 0.0, "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() apply_on = doc.apply_on.replace(' ', '_').lower()
child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'} child_table = {'Item Code': 'items', 'Item Group': 'item_groups', 'Brand': 'brands'}
doc.append(child_table.get(doc.apply_on), { doc.append(child_table.get(doc.apply_on), {

View File

@@ -14,9 +14,8 @@ import frappe
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups 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.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor from erpnext.stock.get_item_details import get_conversion_factor
from frappe import _, throw from frappe import _, bold
from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today from frappe.utils import cint, flt, get_link_to_form, getdate, today, fmt_money
class MultiplePricingRuleConflict(frappe.ValidationError): pass class MultiplePricingRuleConflict(frappe.ValidationError): pass
@@ -42,6 +41,7 @@ def get_pricing_rules(args, doc=None):
if not pricing_rules: return [] if not pricing_rules: return []
if apply_multiple_pricing_rules(pricing_rules): if apply_multiple_pricing_rules(pricing_rules):
pricing_rules = sorted_by_priority(pricing_rules)
for pricing_rule in pricing_rules: for pricing_rule in pricing_rules:
pricing_rule = filter_pricing_rules(args, pricing_rule, doc) pricing_rule = filter_pricing_rules(args, pricing_rule, doc)
if pricing_rule: if pricing_rule:
@@ -53,6 +53,20 @@ def get_pricing_rules(args, doc=None):
return rules 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): def filter_pricing_rule_based_on_condition(pricing_rules, doc=None):
filtered_pricing_rules = [] filtered_pricing_rules = []
if doc: if doc:
@@ -284,12 +298,13 @@ def validate_quantity_and_amount_for_suggestion(args, qty, amount, item_code, tr
fieldname = field fieldname = field
if fieldname: if fieldname:
msg = _("""If you {0} {1} quantities of the item <b>{2}</b>, the scheme <b>{3}</b> msg = (_("If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.")
will be applied on the item.""").format(type_of_transaction, args.get(fieldname), item_code, args.rule_description) .format(type_of_transaction, args.get(fieldname), bold(item_code), bold(args.rule_description)))
if fieldname in ['min_amt', 'max_amt']: 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. msg = (_("If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.")
""").format(frappe.fmt_money(type_of_transaction, args.get(fieldname)), item_code, args.rule_description) .format(type_of_transaction, fmt_money(args.get(fieldname), currency=args.get("currency")),
bold(item_code), bold(args.rule_description)))
frappe.msgprint(msg) frappe.msgprint(msg)

View File

@@ -237,7 +237,7 @@ class TestSubscription(unittest.TestCase):
subscription.party_type = 'Customer' subscription.party_type = 'Customer'
subscription.party = '_Test Customer' subscription.party = '_Test Customer'
subscription.append('plans', {'plan': '_Test Plan Name', 'qty': 1}) 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.insert()
subscription.process() # generate first invoice subscription.process() # generate first invoice

View File

@@ -63,6 +63,7 @@ def get_pos_entries(filters, group_by_field):
FROM FROM
`tabPOS Invoice` p {from_sales_invoice_payment} `tabPOS Invoice` p {from_sales_invoice_payment}
WHERE WHERE
p.docstatus = 1 and
{group_by_mop_condition} {group_by_mop_condition}
{conditions} {conditions}
ORDER BY ORDER BY

View File

@@ -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--\"}", "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", "group_by_type": "Count",
"idx": 0, "idx": 0,
"is_public": 0, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"modified": "2020-07-23 13:53:33.211371", "modified": "2020-10-28 23:15:58.432189",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Value Analytics", "name": "Asset Value Analytics",

View File

@@ -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()\"}", "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}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Asset Category\",\"is_existing_asset\":0}",
"idx": 0, "idx": 0,
"is_public": 0, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"modified": "2020-07-23 13:39:32.429240", "modified": "2020-10-28 23:16:16.939070",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Category-wise Asset Value", "name": "Category-wise Asset Value",

View File

@@ -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()\"}", "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}", "filters_json": "{\"status\":\"In Location\",\"group_by\":\"Location\",\"is_existing_asset\":0}",
"idx": 0, "idx": 0,
"is_public": 0, "is_public": 1,
"is_standard": 1, "is_standard": 1,
"modified": "2020-07-23 13:42:44.912551", "modified": "2020-10-28 23:16:07.883312",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Location-wise Asset Value", "name": "Location-wise Asset Value",

View File

@@ -55,6 +55,7 @@
"fieldtype": "Date", "fieldtype": "Date",
"in_list_view": 1, "in_list_view": 1,
"label": "Depreciation Posting Date", "label": "Depreciation Posting Date",
"mandatory_depends_on": "eval:parent.doctype == 'Asset'",
"reqd": 1 "reqd": 1
}, },
{ {
@@ -86,7 +87,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-09-16 12:11:30.631788", "modified": "2020-10-30 15:22:29.119868",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Assets", "module": "Assets",
"name": "Asset Finance Book", "name": "Asset Finance Book",

View File

@@ -876,7 +876,7 @@ class TestPurchaseOrder(unittest.TestCase):
}, },
{ {
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item", "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 @@ class TestPurchaseOrder(unittest.TestCase):
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string)) se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
se.submit() 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) po_doc = frappe.get_doc("Purchase Order", po.name)
for row in po_doc.supplied_items: for row in po_doc.supplied_items:
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order # Valid that whether transferred quantity is matching with supplied qty or not in the purchase order

View File

@@ -304,7 +304,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
{ {
"fieldtype": "Select", "label": __("Get Suppliers By"), "fieldtype": "Select", "label": __("Get Suppliers By"),
"fieldname": "search_type", "fieldname": "search_type",
"options": ["Tag","Supplier Group"], "options": ["Supplier Group", "Tag"],
"reqd": 1, "reqd": 1,
onchange() { onchange() {
if(dialog.get_value('search_type') == 'Tag'){ if(dialog.get_value('search_type') == 'Tag'){

View File

@@ -21,9 +21,9 @@
"link_to_mrs", "link_to_mrs",
"supplier_response_section", "supplier_response_section",
"salutation", "salutation",
"email_template",
"col_break_email_1",
"subject", "subject",
"col_break_email_1",
"email_template",
"preview", "preview",
"sec_break_email_2", "sec_break_email_2",
"message_for_supplier", "message_for_supplier",
@@ -260,7 +260,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-10-16 17:49:09.561929", "modified": "2020-11-04 22:04:29.017134",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Request for Quotation", "name": "Request for Quotation",

View File

@@ -5,14 +5,14 @@
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"send_email",
"email_sent",
"supplier", "supplier",
"contact", "contact",
"quote_status", "quote_status",
"column_break_3", "column_break_3",
"supplier_name", "supplier_name",
"email_id" "email_id",
"send_email",
"email_sent"
], ],
"fields": [ "fields": [
{ {
@@ -87,7 +87,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-10-16 12:23:41.769820", "modified": "2020-11-04 22:01:43.832942",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Request for Quotation Supplier", "name": "Request for Quotation Supplier",

View File

@@ -263,6 +263,7 @@ class AccountsController(TransactionBase):
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"): if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
parent_dict.update({"customer": parent_dict.get("party_name")}) parent_dict.update({"customer": parent_dict.get("party_name")})
self.pricing_rules = []
for item in self.get("items"): for item in self.get("items"):
if item.get("item_code"): if item.get("item_code"):
args = parent_dict.copy() args = parent_dict.copy()
@@ -301,6 +302,7 @@ class AccountsController(TransactionBase):
if ret.get("pricing_rules"): if ret.get("pricing_rules"):
self.apply_pricing_rule_on_items(item, ret) self.apply_pricing_rule_on_items(item, ret)
self.set_pricing_rule_details(item, ret)
if self.doctype == "Purchase Invoice": if self.doctype == "Purchase Invoice":
self.set_expense_account(for_validate) self.set_expense_account(for_validate)
@@ -322,6 +324,9 @@ class AccountsController(TransactionBase):
if item.get('discount_amount'): if item.get('discount_amount'):
item.rate = item.price_list_rate - item.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'): elif pricing_rule_args.get('free_item_data'):
apply_pricing_rule_for_free_items(self, 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 @@ class AccountsController(TransactionBase):
frappe.msgprint(_("Row {0}: user has not applied the rule {1} on the item {2}") 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))) .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): def set_taxes(self):
if not self.meta.get_field("taxes"): if not self.meta.get_field("taxes"):
return return

View File

@@ -301,7 +301,7 @@ class BuyingController(StockController):
# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code) # 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: 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, {}) raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
consumed_qty = raw_material_data.get('qty', 0) consumed_qty = raw_material_data.get('qty', 0)
@@ -910,7 +910,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders):
purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references) purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
for data in purchase_receipt_supplied_items: 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: if pr_key not in backflushed_raw_materials_map:
backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({ backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
"qty": 0.0, "qty": 0.0,
@@ -936,7 +936,7 @@ def get_backflushed_subcontracted_raw_materials(purchase_orders):
def get_supplied_items(item_code, purchase_receipt, references): def get_supplied_items(item_code, purchase_receipt, references):
return frappe.get_all("Purchase Receipt Item Supplied", 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)}) filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
def get_asset_item_details(asset_items): def get_asset_item_details(asset_items):

View File

@@ -608,16 +608,19 @@ class calculate_taxes_and_totals(object):
base_rate_with_margin = 0.0 base_rate_with_margin = 0.0
if item.price_list_rate: if item.price_list_rate:
if item.pricing_rules and not self.doc.ignore_pricing_rule: if item.pricing_rules and not self.doc.ignore_pricing_rule:
has_margin = False
for d in get_applied_pricing_rules(item.pricing_rules): for d in get_applied_pricing_rules(item.pricing_rules):
pricing_rule = frappe.get_cached_doc('Pricing Rule', d) 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'): or (pricing_rule.margin_type == 'Percentage'):
item.margin_type = pricing_rule.margin_type item.margin_type = pricing_rule.margin_type
item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount item.margin_rate_or_amount = pricing_rule.margin_rate_or_amount
else: has_margin = True
item.margin_type = None
item.margin_rate_or_amount = 0.0 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: 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 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

View File

@@ -75,6 +75,6 @@ class StudentAttendance(Document):
}) })
if attendance_record: 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}') frappe.throw(_('Student Attendance record {0} already exists against the Student {1}')
.format(record, frappe.bold(self.student)), title=_('Duplicate Entry')) .format(record, frappe.bold(self.student)), title=_('Duplicate Entry'))

View File

@@ -8,7 +8,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Payments", "label": "Payments",
"links": "[\n {\n \"description\": \"GoCardless payment gateway settings\",\n \"label\": \"GoCardless Settings\",\n \"name\": \"GoCardless Settings\",\n \"type\": \"doctype\"\n }\n]" "links": "[\n {\n \"description\": \"GoCardless payment gateway settings\",\n \"label\": \"GoCardless Settings\",\n \"name\": \"GoCardless Settings\",\n \"type\": \"doctype\"\n }, {\n \"description\": \"M-Pesa payment gateway settings\",\n \"label\": \"M-Pesa Settings\",\n \"name\": \"Mpesa Settings\",\n \"type\": \"doctype\"\n }\n]"
}, },
{ {
"hidden": 0, "hidden": 0,
@@ -29,7 +29,7 @@
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "ERPNext Integrations", "label": "ERPNext Integrations",
"modified": "2020-08-23 16:30:51.494655", "modified": "2020-10-29 19:54:46.228222",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "ERPNext Integrations", "module": "ERPNext Integrations",
"name": "ERPNext Integrations", "name": "ERPNext Integrations",

View File

@@ -9,11 +9,12 @@ frappe.ui.form.on('Mpesa Settings', {
refresh: function(frm) { refresh: function(frm) {
frappe.realtime.on("refresh_mpesa_dashboard", function(){ frappe.realtime.on("refresh_mpesa_dashboard", function(){
frm.reload_doc(); frm.reload_doc();
frm.events.setup_account_balance_html(frm);
}); });
}, },
get_account_balance: function(frm) { get_account_balance: function(frm) {
if (!frm.initiator_name && !frm.security_credentials) { if (!frm.doc.initiator_name && !frm.doc.security_credential) {
frappe.throw(__("Please set the initiator name and the security credential")); frappe.throw(__("Please set the initiator name and the security credential"));
} }
frappe.call({ frappe.call({

View File

@@ -147,7 +147,7 @@ def get_account_balance(request_payload):
return response return response
except Exception: except Exception:
frappe.log_error(title=_("Account Balance Processing Error")) frappe.log_error(title=_("Account Balance Processing Error"))
frappe.throw(title=_("Error"), message=_("Please check your configuration and try again")) frappe.throw(_("Please check your configuration and try again"), title=_("Error"))
@frappe.whitelist(allow_guest=True) @frappe.whitelist(allow_guest=True)
def process_balance_info(**kwargs): def process_balance_info(**kwargs):
@@ -173,7 +173,8 @@ def process_balance_info(**kwargs):
ref_doc.db_set("account_balance", balance_info) ref_doc.db_set("account_balance", balance_info)
request.handle_success(account_balance_response) request.handle_success(account_balance_response)
frappe.publish_realtime("refresh_mpesa_dashboard") frappe.publish_realtime("refresh_mpesa_dashboard", doctype="Mpesa Settings",
docname=transaction_data.reference_docname, user=transaction_data.owner)
except Exception: except Exception:
request.handle_failure(account_balance_response) request.handle_failure(account_balance_response)
frappe.log_error(title=_("Mpesa Account Balance Processing Error"), message=account_balance_response) frappe.log_error(title=_("Mpesa Account Balance Processing Error"), message=account_balance_response)

View File

@@ -31,6 +31,7 @@ class PlaidConnector():
return access_token return access_token
def get_link_token(self): def get_link_token(self):
country_codes = ["US", "CA", "FR", "IE", "NL", "ES", "GB"] if self.settings.enable_european_access else ["US", "CA"]
token_request = { token_request = {
"client_name": self.client_name, "client_name": self.client_name,
"client_id": self.settings.plaid_client_id, "client_id": self.settings.plaid_client_id,
@@ -38,7 +39,7 @@ class PlaidConnector():
"products": self.products, "products": self.products,
# only allow Plaid-supported languages and countries (LAST: Sep-19-2020) # only allow Plaid-supported languages and countries (LAST: Sep-19-2020)
"language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en", "language": frappe.local.lang if frappe.local.lang in ["en", "fr", "es", "nl"] else "en",
"country_codes": ["US", "CA", "FR", "IE", "NL", "ES", "GB"], "country_codes": country_codes,
"user": { "user": {
"client_user_id": frappe.generate_hash(frappe.session.user, length=32) "client_user_id": frappe.generate_hash(frappe.session.user, length=32)
} }

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2018-10-25 10:02:48.656165", "creation": "2018-10-25 10:02:48.656165",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -11,7 +12,8 @@
"plaid_client_id", "plaid_client_id",
"plaid_secret", "plaid_secret",
"column_break_7", "column_break_7",
"plaid_env" "plaid_env",
"enable_european_access"
], ],
"fields": [ "fields": [
{ {
@@ -58,10 +60,17 @@
{ {
"fieldname": "column_break_7", "fieldname": "column_break_7",
"fieldtype": "Column Break" "fieldtype": "Column Break"
},
{
"default": "0",
"fieldname": "enable_european_access",
"fieldtype": "Check",
"label": "Enable European Access"
} }
], ],
"issingle": 1, "issingle": 1,
"modified": "2020-09-12 02:31:44.542385", "links": [],
"modified": "2020-10-29 20:24:56.916104",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "ERPNext Integrations", "module": "ERPNext Integrations",
"name": "Plaid Settings", "name": "Plaid Settings",

View File

@@ -37,7 +37,8 @@
"depends_on": "eval:doc.parenttype==\"Therapy\";", "depends_on": "eval:doc.parenttype==\"Therapy\";",
"fieldname": "counts_completed", "fieldname": "counts_completed",
"fieldtype": "Int", "fieldtype": "Int",
"label": "Counts Completed" "label": "Counts Completed",
"no_copy": 1
}, },
{ {
"fieldname": "assistance_level", "fieldname": "assistance_level",
@@ -48,7 +49,7 @@
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-04-10 13:41:06.662351", "modified": "2020-11-04 18:20:25.583491",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Exercise", "name": "Exercise",

View File

@@ -21,6 +21,19 @@ frappe.ui.form.on('Inpatient Medication Entry', {
} }
}; };
}); });
frm.set_query('warehouse', () => {
return {
filters: {
company: frm.doc.company
}
};
});
},
patient: function(frm) {
if (frm.doc.patient)
frm.set_value('service_unit', '');
}, },
get_medication_orders: function(frm) { get_medication_orders: function(frm) {

View File

@@ -67,6 +67,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval: doc.__islocal",
"fieldname": "filters_section", "fieldname": "filters_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Filters" "label": "Filters"
@@ -93,6 +94,7 @@
"options": "Patient" "options": "Patient"
}, },
{ {
"depends_on": "eval:!doc.patient",
"fieldname": "service_unit", "fieldname": "service_unit",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Healthcare Service Unit", "label": "Healthcare Service Unit",
@@ -178,7 +180,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-09-30 23:40:45.528715", "modified": "2020-11-03 13:22:37.820707",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Inpatient Medication Entry", "name": "Inpatient Medication Entry",

View File

@@ -199,6 +199,7 @@ class InpatientMedicationEntry(Document):
def get_pending_medication_orders(entry): def get_pending_medication_orders(entry):
filters, values = get_filters(entry) filters, values = get_filters(entry)
to_remove = []
data = frappe.db.sql(""" data = frappe.db.sql("""
SELECT SELECT
@@ -225,7 +226,10 @@ def get_pending_medication_orders(entry):
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record) doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
if entry.service_unit and doc.service_unit != entry.service_unit: if entry.service_unit and doc.service_unit != entry.service_unit:
data.remove(doc) to_remove.append(doc)
for doc in to_remove:
data.remove(doc)
return data return data

View File

@@ -12,7 +12,8 @@ frappe.ui.form.on('Inpatient Medication Order', {
frm.set_query('patient', () => { frm.set_query('patient', () => {
return { return {
filters: { filters: {
'inpatient_record': ['!=', ''] 'inpatient_record': ['!=', ''],
'inpatient_status': 'Admitted'
} }
}; };
}); });

View File

@@ -13,43 +13,42 @@ frappe.ui.form.on('Therapy Plan', {
refresh: function(frm) { refresh: function(frm) {
if (!frm.doc.__islocal) { if (!frm.doc.__islocal) {
frm.trigger('show_progress_for_therapies'); frm.trigger('show_progress_for_therapies');
} if (frm.doc.status != 'Completed') {
let therapy_types = (frm.doc.therapy_plan_details || []).map(function(d){ return d.therapy_type; });
if (!frm.doc.__islocal && frm.doc.status != 'Completed') { const fields = [{
let therapy_types = (frm.doc.therapy_plan_details || []).map(function(d){ return d.therapy_type }); fieldtype: 'Link',
const fields = [{ label: __('Therapy Type'),
fieldtype: 'Link', fieldname: 'therapy_type',
label: __('Therapy Type'), options: 'Therapy Type',
fieldname: 'therapy_type', reqd: 1,
options: 'Therapy Type', get_query: function() {
reqd: 1, return {
get_query: function() { filters: { 'therapy_type': ['in', therapy_types]}
return { };
filters: { 'therapy_type': ['in', therapy_types]}
} }
} }];
}];
frm.add_custom_button(__('Therapy Session'), function() { frm.add_custom_button(__('Therapy Session'), function() {
frappe.prompt(fields, data => { frappe.prompt(fields, data => {
frappe.call({ frappe.call({
method: 'erpnext.healthcare.doctype.therapy_plan.therapy_plan.make_therapy_session', method: 'erpnext.healthcare.doctype.therapy_plan.therapy_plan.make_therapy_session',
args: { args: {
therapy_plan: frm.doc.name, therapy_plan: frm.doc.name,
patient: frm.doc.patient, patient: frm.doc.patient,
therapy_type: data.therapy_type, therapy_type: data.therapy_type,
company: frm.doc.company company: frm.doc.company
}, },
freeze: true, freeze: true,
callback: function(r) { callback: function(r) {
if (r.message) { if (r.message) {
frappe.model.sync(r.message); frappe.model.sync(r.message);
frappe.set_route('Form', r.message.doctype, r.message.name); frappe.set_route('Form', r.message.doctype, r.message.name);
}
} }
} });
}); }, __('Select Therapy Type'), __('Create'));
}, __('Select Therapy Type'), __('Create')); }, __('Create'));
}, __('Create')); }
if (frm.doc.therapy_plan_template && !frm.doc.invoiced) { if (frm.doc.therapy_plan_template && !frm.doc.invoiced) {
frm.add_custom_button(__('Sales Invoice'), function() { frm.add_custom_button(__('Sales Invoice'), function() {

View File

@@ -115,7 +115,8 @@
"fieldname": "therapy_plan_template", "fieldname": "therapy_plan_template",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Therapy Plan Template", "label": "Therapy Plan Template",
"options": "Therapy Plan Template" "options": "Therapy Plan Template",
"set_only_once": 1
}, },
{ {
"default": "0", "default": "0",
@@ -128,7 +129,7 @@
} }
], ],
"links": [], "links": [],
"modified": "2020-10-23 01:27:42.128855", "modified": "2020-11-04 18:13:13.564999",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Therapy Plan", "name": "Therapy Plan",

View File

@@ -30,12 +30,13 @@
"fieldname": "sessions_completed", "fieldname": "sessions_completed",
"fieldtype": "Int", "fieldtype": "Int",
"label": "Sessions Completed", "label": "Sessions Completed",
"no_copy": 1,
"read_only": 1 "read_only": 1
} }
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-10-08 01:17:34.778028", "modified": "2020-11-04 18:15:52.173450",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Therapy Plan Detail", "name": "Therapy Plan Detail",

View File

@@ -22,6 +22,10 @@ frappe.ui.form.on('Therapy Session', {
}, },
refresh: function(frm) { refresh: function(frm) {
if (frm.doc.therapy_plan) {
frm.trigger('filter_therapy_types');
}
if (!frm.doc.__islocal) { if (!frm.doc.__islocal) {
frm.dashboard.add_indicator(__('Counts Targeted: {0}', [frm.doc.total_counts_targeted]), 'blue'); frm.dashboard.add_indicator(__('Counts Targeted: {0}', [frm.doc.total_counts_targeted]), 'blue');
frm.dashboard.add_indicator(__('Counts Completed: {0}', [frm.doc.total_counts_completed]), frm.dashboard.add_indicator(__('Counts Completed: {0}', [frm.doc.total_counts_completed]),
@@ -36,15 +40,43 @@ frappe.ui.form.on('Therapy Session', {
}) })
}, 'Create'); }, 'Create');
frm.add_custom_button(__('Sales Invoice'), function() { frappe.db.get_value('Therapy Plan', {'name': frm.doc.therapy_plan}, 'therapy_plan_template', (r) => {
frappe.model.open_mapped_doc({ if (r && !r.therapy_plan_template) {
method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.invoice_therapy_session', frm.add_custom_button(__('Sales Invoice'), function() {
frm: frm, frappe.model.open_mapped_doc({
}) method: 'erpnext.healthcare.doctype.therapy_session.therapy_session.invoice_therapy_session',
}, 'Create'); frm: frm,
});
}, 'Create');
}
});
} }
}, },
therapy_plan: function(frm) {
if (frm.doc.therapy_plan) {
frm.trigger('filter_therapy_types');
}
},
filter_therapy_types: function(frm) {
frappe.call({
'method': 'frappe.client.get',
args: {
doctype: 'Therapy Plan',
name: frm.doc.therapy_plan
},
callback: function(data) {
let therapy_types = (data.message.therapy_plan_details || []).map(function(d){ return d.therapy_type; });
frm.set_query('therapy_type', function() {
return {
filters: { 'therapy_type': ['in', therapy_types]}
};
});
}
});
},
patient: function(frm) { patient: function(frm) {
if (frm.doc.patient) { if (frm.doc.patient) {
frappe.call({ frappe.call({
@@ -98,19 +130,6 @@ frappe.ui.form.on('Therapy Session', {
frm.set_value(values); frm.set_value(values);
} }
}); });
} else {
let values = {
'patient': '',
'therapy_type': '',
'therapy_plan': '',
'practitioner': '',
'department': '',
'start_date': '',
'start_time': '',
'service_unit': '',
'duration': ''
};
frm.set_value(values);
} }
}, },

View File

@@ -194,6 +194,7 @@
"fieldname": "total_counts_completed", "fieldname": "total_counts_completed",
"fieldtype": "Int", "fieldtype": "Int",
"label": "Total Counts Completed", "label": "Total Counts Completed",
"no_copy": 1,
"read_only": 1 "read_only": 1
}, },
{ {
@@ -222,7 +223,7 @@
], ],
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-10-22 23:10:21.178644", "modified": "2020-11-04 18:14:25.999939",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Healthcare", "module": "Healthcare",
"name": "Therapy Session", "name": "Therapy Session",

View File

@@ -56,23 +56,35 @@ frappe.ui.form.on('Production Plan', {
refresh: function(frm) { refresh: function(frm) {
if (frm.doc.docstatus === 1) { if (frm.doc.docstatus === 1) {
frm.trigger("show_progress"); 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 if (frm.doc.status !== "Closed") {
&& frm.doc.status != 'Completed') { frm.page.set_inner_btn_group_as_primary(__('Create'));
frm.add_custom_button(__("Work Order"), ()=> {
frm.trigger("make_work_order");
}, __('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"); frm.trigger("material_requirement");
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;"> const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
@@ -121,6 +133,18 @@ frappe.ui.form.on('Production Plan', {
set_field_options("projected_qty_formula", projected_qty_formula); 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) { make_work_order: function(frm) {
frappe.call({ frappe.call({
method: "make_work_order", method: "make_work_order",

View File

@@ -275,7 +275,7 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"no_copy": 1, "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, "print_hide": 1,
"read_only": 1 "read_only": 1
}, },
@@ -304,9 +304,10 @@
} }
], ],
"icon": "fa fa-calendar", "icon": "fa fa-calendar",
"index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-02-03 00:25:25.934202", "modified": "2020-10-26 13:00:54.335319",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan", "name": "Production Plan",

View File

@@ -219,13 +219,17 @@ class ProductionPlan(Document):
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}): filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
frappe.delete_doc('Work Order', d.name) frappe.delete_doc('Work Order', d.name)
def set_status(self): def set_status(self, close=None):
self.status = { self.status = {
0: 'Draft', 0: 'Draft',
1: 'Submitted', 1: 'Submitted',
2: 'Cancelled' 2: 'Cancelled'
}.get(self.docstatus) }.get(self.docstatus)
if close:
self.db_set('status', 'Closed')
return
if self.total_produced_qty > 0: if self.total_produced_qty > 0:
self.status = "In Process" self.status = "In Process"
if self.total_produced_qty == self.total_planned_qty: if self.total_produced_qty == self.total_planned_qty:
@@ -235,6 +239,9 @@ class ProductionPlan(Document):
self.update_ordered_status() self.update_ordered_status()
self.update_requested_status() self.update_requested_status()
if close is not None:
self.db_set('status', self.status)
def update_ordered_status(self): def update_ordered_status(self):
update_status = False update_status = False
for d in self.po_items: for d in self.po_items:
@@ -735,10 +742,12 @@ def get_items_for_material_requests(doc, warehouses=None):
mr_items = new_mr_items mr_items = new_mr_items
if not mr_items: if not mr_items:
frappe.msgprint(_("""As raw materials projected quantity is more than required quantity, to_enable = frappe.bold(_("Ignore Existing Projected Quantity"))
there is no need to create material request for the warehouse {0}. warehouse = frappe.bold(doc.get('for_warehouse'))
Still if you want to make material request, message = _("As there are sufficient raw materials, Material Request is not required for Warehouse {0}.").format(warehouse) + "<br><br>"
kindly enable <b>Ignore Existing Projected Quantity</b> checkbox""").format(doc.get('for_warehouse'))) message += _(" If you still want to proceed, please enable {0}.").format(to_enable)
frappe.msgprint(message, title=_("Note"))
return mr_items return mr_items

View File

@@ -1,6 +1,6 @@
frappe.listview_settings['Production Plan'] = { frappe.listview_settings['Production Plan'] = {
add_fields: ["status"], add_fields: ["status"],
filters: [["status", "!=", "Stopped"]], filters: [["status", "!=", "Closed"]],
get_indicator: function(doc) { get_indicator: function(doc) {
if(doc.status==="Submitted") { if(doc.status==="Submitted") {
return [__("Not Started"), "orange", "status,=,Submitted"]; return [__("Not Started"), "orange", "status,=,Submitted"];
@@ -10,7 +10,8 @@ frappe.listview_settings['Production Plan'] = {
"In Process": "orange", "In Process": "orange",
"Completed": "green", "Completed": "green",
"Material Requested": "darkgrey", "Material Requested": "darkgrey",
"Cancelled": "darkgrey" "Cancelled": "darkgrey",
"Closed": "grey"
}[doc.status], "status,=," + doc.status]; }[doc.status], "status,=," + doc.status];
} }
} }

View File

@@ -11,30 +11,20 @@
{ {
"fieldname": "warehouse", "fieldname": "warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1,
"label": "Warehouse", "label": "Warehouse",
"options": "Warehouse" "options": "Warehouse"
} }
], ],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [], "links": [],
"modified": "2020-02-02 10:37:16.650836", "modified": "2020-10-26 12:55:00.778201",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan Material Request Warehouse", "name": "Production Plan Material Request Warehouse",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [],
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",

View File

@@ -2,7 +2,15 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Routing', { frappe.ui.form.on('Routing', {
setup: function(frm) { refresh: function(frm) {
frm.trigger("display_sequence_id_column");
},
onload: function(frm) {
frm.trigger("display_sequence_id_column");
},
display_sequence_id_column: function(frm) {
frappe.meta.get_docfield("BOM Operation", "sequence_id", frappe.meta.get_docfield("BOM Operation", "sequence_id",
frm.doc.name).in_list_view = true; frm.doc.name).in_list_view = true;

View File

@@ -732,3 +732,5 @@ erpnext.patches.v13_0.set_youtube_video_id
erpnext.patches.v13_0.print_uom_after_quantity_patch erpnext.patches.v13_0.print_uom_after_quantity_patch
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account 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.create_healthcare_custom_fields_in_stock_entry_detail
erpnext.patches.v13_0.update_reason_for_resignation_in_employee
execute:frappe.delete_doc("Report", "Quoted Item Comparison")

View File

@@ -53,7 +53,7 @@ def execute():
# renamed reports from "Minutes to First Response for Issues" to "First Response Time for Issues". Same for Opportunity # 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']: for report in ['Minutes to First Response for Issues', 'Minutes to First Response for Opportunity']:
if frappe.db.exists('Report', report): 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): def convert_to_seconds(value, unit):

View File

@@ -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
""")

View File

@@ -352,9 +352,15 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
let show_description = function(idx, exist = null) { let show_description = function(idx, exist = null) {
if (exist) { 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 { } 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 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}).then(r => { }).then(r => {
const data = r && r.message; const data = r && r.message;
if (!data || Object.keys(data).length === 0) { 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; return;
} }
@@ -651,7 +660,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
let child = frappe.model.add_child(me.frm.doc, "taxes"); let child = frappe.model.add_child(me.frm.doc, "taxes");
child.charge_type = "On Net Total"; child.charge_type = "On Net Total";
child.account_head = tax; child.account_head = tax;
child.rate = rate; child.rate = 0;
} }
}); });
} }

View File

@@ -18,7 +18,7 @@
{ {
"hidden": 0, "hidden": 0,
"label": "Review and Action", "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", "category": "Modules",
@@ -29,11 +29,11 @@
"docstatus": 0, "docstatus": 0,
"doctype": "Desk Page", "doctype": "Desk Page",
"extends_another_page": 0, "extends_another_page": 0,
"icon": "", "hide_custom": 0,
"idx": 0, "idx": 0,
"is_standard": 1, "is_standard": 1,
"label": "Quality", "label": "Quality",
"modified": "2020-04-01 11:28:51.095012", "modified": "2020-10-27 16:28:54.138055",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality", "name": "Quality",
@@ -47,6 +47,7 @@
"type": "DocType" "type": "DocType"
}, },
{ {
"doc_view": "Tree",
"label": "Quality Procedure", "label": "Quality Procedure",
"link_to": "Quality Procedure", "link_to": "Quality Procedure",
"type": "DocType" "type": "DocType"
@@ -55,6 +56,33 @@
"label": "Quality Inspection", "label": "Quality Inspection",
"link_to": "Quality Inspection", "link_to": "Quality Inspection",
"type": "DocType" "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"
} }
] ]
} }

View File

@@ -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) {
// }
});

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -2,32 +2,5 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Quality Action', { 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();
}
});
},
}); });

View File

@@ -1,32 +1,34 @@
{ {
"autoname": "format:ACTN-{#####}", "actions": [],
"autoname": "format:QA-ACT-{#####}",
"creation": "2018-10-02 11:40:43.666100", "creation": "2018-10-02 11:40:43.666100",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"corrective_preventive", "corrective_preventive",
"document_type", "review",
"goal", "feedback",
"status",
"cb_00", "cb_00",
"date", "date",
"document_name", "goal",
"procedure", "procedure",
"status",
"sb_00", "sb_00",
"resolutions" "resolutions"
], ],
"fields": [ "fields": [
{ {
"depends_on": "eval:doc.type == 'Quality Review'",
"fetch_from": "review.goal", "fetch_from": "review.goal",
"fieldname": "goal", "fieldname": "goal",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Goal", "label": "Goal",
"options": "Quality Goal", "options": "Quality Goal"
"read_only": 1
}, },
{ {
"default": "Today",
"fieldname": "date", "fieldname": "date",
"fieldtype": "Date", "fieldtype": "Date",
"in_list_view": 1, "in_list_view": 1,
@@ -34,34 +36,20 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:doc.type == 'Quality Review'",
"fieldname": "procedure", "fieldname": "procedure",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Procedure", "label": "Procedure",
"options": "Quality Procedure", "options": "Quality Procedure"
"read_only": 1
}, },
{ {
"default": "Open", "default": "Open",
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"in_list_view": 1, "in_list_view": 1,
"in_standard_filter": 1,
"label": "Status", "label": "Status",
"options": "Open\nClosed" "options": "Open\nCompleted",
}, "read_only": 1
{
"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
}, },
{ {
"default": "Corrective", "default": "Corrective",
@@ -86,9 +74,24 @@
"fieldtype": "Table", "fieldtype": "Table",
"label": "Resolutions", "label": "Resolutions",
"options": "Quality Action Resolution" "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", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Action", "name": "Quality Action",

View File

@@ -7,4 +7,5 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
class QualityAction(Document): class QualityAction(Document):
pass def validate(self):
self.status = 'Open' if any([d.status=='Open' for d in self.resolutions]) else 'Completed'

View File

@@ -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()
]);
});

View File

@@ -5,42 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest 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): class TestQualityAction(unittest.TestCase):
# quality action has no code
def test_quality_action(self): pass
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})

View File

@@ -1,33 +1,54 @@
{ {
"actions": [],
"creation": "2019-05-26 20:36:44.337186", "creation": "2019-05-26 20:36:44.337186",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"problem", "problem",
"sb_00", "resolution",
"resolution" "status",
"responsible",
"completion_by"
], ],
"fields": [ "fields": [
{ {
"fieldname": "problem", "fieldname": "problem",
"fieldtype": "Long Text", "fieldtype": "Long Text",
"in_list_view": 1, "in_list_view": 1,
"label": "Review" "label": "Problem"
},
{
"fieldname": "sb_00",
"fieldtype": "Section Break"
}, },
{ {
"fieldname": "resolution", "fieldname": "resolution",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"in_list_view": 1, "in_list_view": 1,
"label": "Resolution" "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, "istable": 1,
"modified": "2019-05-28 13:09:50.435323", "links": [],
"modified": "2020-10-21 12:59:25.566682",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Action Resolution", "name": "Quality Action Resolution",

View File

@@ -2,31 +2,9 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Quality Feedback', { frappe.ui.form.on('Quality Feedback', {
refresh: function(frm) {
frm.set_value("date", frappe.datetime.get_today());
},
template: function(frm) { template: function(frm) {
if (frm.doc.template) { if (frm.doc.template) {
frappe.call({ frm.call('set_parameters');
"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();
}
}
});
} }
} }
}); });

View File

@@ -1,16 +1,15 @@
{ {
"actions": [], "actions": [],
"autoname": "format:FDBK-{#####}", "autoname": "format:QA-FB-{#####}",
"creation": "2019-05-26 21:23:05.308379", "creation": "2019-05-26 21:23:05.308379",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"document_type",
"template", "template",
"cb_00", "cb_00",
"document_type",
"document_name", "document_name",
"date",
"sb_00", "sb_00",
"parameters" "parameters"
], ],
@@ -18,6 +17,7 @@
{ {
"fieldname": "template", "fieldname": "template",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1,
"label": "Template", "label": "Template",
"options": "Quality Feedback Template", "options": "Quality Feedback Template",
"reqd": 1 "reqd": 1
@@ -26,13 +26,6 @@
"fieldname": "cb_00", "fieldname": "cb_00",
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date",
"read_only": 1
},
{ {
"fieldname": "sb_00", "fieldname": "sb_00",
"fieldtype": "Section Break" "fieldtype": "Section Break"
@@ -47,6 +40,7 @@
{ {
"fieldname": "document_type", "fieldname": "document_type",
"fieldtype": "Select", "fieldtype": "Select",
"in_list_view": 1,
"label": "Type", "label": "Type",
"options": "User\nCustomer", "options": "User\nCustomer",
"reqd": 1 "reqd": 1
@@ -54,13 +48,20 @@
{ {
"fieldname": "document_name", "fieldname": "document_name",
"fieldtype": "Dynamic Link", "fieldtype": "Dynamic Link",
"in_list_view": 1,
"label": "Feedback By", "label": "Feedback By",
"options": "document_type", "options": "document_type",
"reqd": 1 "reqd": 1
} }
], ],
"links": [], "links": [
"modified": "2020-07-03 15:50:58.589302", {
"group": "Actions",
"link_doctype": "Quality Action",
"link_fieldname": "feedback"
}
],
"modified": "2020-10-27 16:20:10.918544",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Feedback", "name": "Quality Feedback",

View File

@@ -7,4 +7,17 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
class QualityFeedback(Document): class QualityFeedback(Document):
pass 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()

View File

@@ -5,49 +5,27 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from erpnext.quality_management.doctype.quality_feedback_template.test_quality_feedback_template import create_template
class TestQualityFeedback(unittest.TestCase): class TestQualityFeedback(unittest.TestCase):
def test_quality_feedback(self): def test_quality_feedback(self):
create_template() template = frappe.get_doc(dict(
test_create_feedback = create_feedback() doctype = 'Quality Feedback Template',
test_get_feedback = get_feedback() 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(): self.assertEqual(template.parameters[0].parameter, feedback.parameters[0].parameter)
create_customer()
feedabck = frappe.get_doc({ feedback.delete()
"doctype": "Quality Feedback", template.delete()
"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)

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2019-05-26 21:25:01.715807", "creation": "2019-05-26 21:25:01.715807",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -39,12 +40,13 @@
"fieldname": "feedback", "fieldname": "feedback",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"in_list_view": 1, "in_list_view": 1,
"label": "Feedback", "label": "Feedback"
"reqd": 1
} }
], ],
"index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"modified": "2019-07-13 19:58:08.966141", "links": [],
"modified": "2020-10-27 17:28:12.033145",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Feedback Parameter", "name": "Quality Feedback Parameter",

View File

@@ -1,13 +1,12 @@
{ {
"actions": [], "actions": [],
"autoname": "format:TMPL-{template}", "autoname": "field:template",
"creation": "2019-05-26 21:17:24.283061", "creation": "2019-05-26 21:17:24.283061",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"template", "template",
"cb_00",
"sb_00", "sb_00",
"parameters" "parameters"
], ],
@@ -16,12 +15,9 @@
"fieldname": "template", "fieldname": "template",
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
"label": "Template", "label": "Template Name",
"reqd": 1 "reqd": 1,
}, "unique": 1
{
"fieldname": "cb_00",
"fieldtype": "Column Break"
}, },
{ {
"fieldname": "sb_00", "fieldname": "sb_00",
@@ -35,8 +31,14 @@
"reqd": 1 "reqd": 1
} }
], ],
"links": [], "links": [
"modified": "2020-07-03 16:06:03.749415", {
"group": "Records",
"link_doctype": "Quality Feedback",
"link_fieldname": "template"
}
],
"modified": "2020-10-27 16:18:53.579688",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Feedback Template", "name": "Quality Feedback Template",

View File

@@ -7,31 +7,4 @@ import frappe
import unittest import unittest
class TestQualityFeedbackTemplate(unittest.TestCase): class TestQualityFeedbackTemplate(unittest.TestCase):
pass
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"})

View File

@@ -2,7 +2,6 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Quality Goal', { frappe.ui.form.on('Quality Goal', {
refresh: function(frm) { // refresh: function(frm) {
frm.doc.created_by = frappe.session.user; // }
}
}); });

View File

@@ -1,5 +1,6 @@
{ {
"autoname": "format:GOAL-{goal}", "actions": [],
"autoname": "field:goal",
"creation": "2018-10-02 12:17:41.727541", "creation": "2018-10-02 12:17:41.727541",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -7,27 +8,14 @@
"field_order": [ "field_order": [
"goal", "goal",
"frequency", "frequency",
"created_by",
"cb_00", "cb_00",
"procedure", "procedure",
"weekday", "weekday",
"quarter",
"date", "date",
"sb_00",
"revision",
"cb_01",
"revised_on",
"sb_01", "sb_01",
"objectives" "objectives"
], ],
"fields": [ "fields": [
{
"fieldname": "created_by",
"fieldtype": "Link",
"label": "Created By",
"options": "User",
"read_only": 1
},
{ {
"default": "None", "default": "None",
"fieldname": "frequency", "fieldname": "frequency",
@@ -50,20 +38,6 @@
"label": "Date", "label": "Date",
"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" "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';", "depends_on": "eval:doc.frequency == 'Weekly';",
"fieldname": "weekday", "fieldname": "weekday",
@@ -75,15 +49,6 @@
"fieldname": "cb_00", "fieldname": "cb_00",
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{
"fieldname": "sb_00",
"fieldtype": "Section Break",
"label": "Revision and Revised On"
},
{
"fieldname": "cb_01",
"fieldtype": "Column Break"
},
{ {
"fieldname": "sb_01", "fieldname": "sb_01",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@@ -101,18 +66,17 @@
"label": "Goal", "label": "Goal",
"reqd": 1, "reqd": 1,
"unique": 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", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Goal", "name": "Quality Goal",

View File

@@ -8,7 +8,5 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
class QualityGoal(Document): class QualityGoal(Document):
def validate(self): def validate(self):
self.revision += 1 pass
self.revised_on = frappe.utils.today()

View File

@@ -1,12 +0,0 @@
from frappe import _
def get_data():
return {
'fieldname': 'goal',
'transactions': [
{
'label': _('Review'),
'items': ['Quality Review']
}
]
}

View File

@@ -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()
]);
});

View File

@@ -8,44 +8,18 @@ import unittest
from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure from erpnext.quality_management.doctype.quality_procedure.test_quality_procedure import create_procedure
class TestQualityGoal(unittest.TestCase): class TestQualityGoal(unittest.TestCase):
def test_quality_goal(self): def test_quality_goal(self):
create_procedure() # no code, just a basic sanity check
create_unit() goal = get_quality_goal()
test_create_goal = create_goal() self.assertTrue(goal)
test_get_goal = get_goal() goal.delete()
self.assertEquals(test_create_goal, test_get_goal) def get_quality_goal():
return frappe.get_doc(dict(
def create_goal(): doctype = 'Quality Goal',
goal = frappe.get_doc({ goal = 'Test Quality Module',
"doctype": "Quality Goal", frequency = 'Daily',
"goal": "_Test Quality Goal", objectives = [
"procedure": "PRC-_Test Quality Procedure", dict(objective = 'Check test cases', target='100', uom='Percent')
"objectives": [
{
"objective": "_Test Quality Objective",
"target": "4",
"uom": "_Test UOM"
}
] ]
}) )).insert()
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()

View File

@@ -2,8 +2,5 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Quality Meeting', { frappe.ui.form.on('Quality Meeting', {
onload: function(frm){
frm.set_value("date", frappe.datetime.get_today());
frm.refresh();
}
}); });

View File

@@ -1,28 +1,19 @@
{ {
"actions": [], "actions": [],
"autoname": "naming_series:", "autoname": "format:QA-MEET-{YY}-{MM}-{DD}",
"creation": "2018-10-15 16:25:41.548432", "creation": "2018-10-15 16:25:41.548432",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"naming_series",
"date",
"cb_00",
"status", "status",
"cb_00",
"sb_00", "sb_00",
"agenda", "agenda",
"sb_01", "sb_01",
"minutes" "minutes"
], ],
"fields": [ "fields": [
{
"fieldname": "date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Date",
"read_only": 1
},
{ {
"default": "Open", "default": "Open",
"fieldname": "status", "fieldname": "status",
@@ -55,16 +46,11 @@
"fieldname": "sb_01", "fieldname": "sb_01",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Minutes" "label": "Minutes"
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Naming Series",
"options": "MTNG-.YYYY.-.MM.-.DD.-"
} }
], ],
"index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2020-05-19 13:18:59.821740", "modified": "2020-10-27 16:36:45.657883",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Meeting", "name": "Quality Meeting",

View File

@@ -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()
]);
});

View File

@@ -5,41 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
from erpnext.quality_management.doctype.quality_review.test_quality_review import create_review
class TestQualityMeeting(unittest.TestCase): class TestQualityMeeting(unittest.TestCase):
def test_quality_meeting(self): # nothing to test
create_review() pass
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

View File

@@ -1,19 +1,22 @@
{ {
"actions": [], "actions": [],
"allow_rename": 1, "allow_rename": 1,
"autoname": "format:PRC-{quality_procedure_name}", "autoname": "field:quality_procedure_name",
"creation": "2018-10-06 00:06:29.756804", "creation": "2018-10-06 00:06:29.756804",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"quality_procedure_name", "quality_procedure_name",
"process_owner",
"process_owner_full_name",
"section_break_3",
"processes",
"sb_00",
"parent_quality_procedure", "parent_quality_procedure",
"is_group", "is_group",
"sb_00",
"processes",
"lft",
"rgt", "rgt",
"lft",
"old_parent" "old_parent"
], ],
"fields": [ "fields": [
@@ -34,14 +37,14 @@
"fieldname": "lft", "fieldname": "lft",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "hidden": 1,
"label": "Lft", "label": "Left Index",
"read_only": 1 "read_only": 1
}, },
{ {
"fieldname": "rgt", "fieldname": "rgt",
"fieldtype": "Int", "fieldtype": "Int",
"hidden": 1, "hidden": 1,
"label": "Rgt", "label": "Right Index",
"read_only": 1 "read_only": 1
}, },
{ {
@@ -54,7 +57,7 @@
{ {
"fieldname": "sb_00", "fieldname": "sb_00",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Processes" "label": "Parent"
}, },
{ {
"fieldname": "processes", "fieldname": "processes",
@@ -67,12 +70,52 @@
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
"label": "Quality Procedure", "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, "is_tree": 1,
"links": [], "links": [
"modified": "2020-10-13 11:46:07.744194", {
"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", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Procedure", "name": "Quality Procedure",

View File

@@ -14,69 +14,58 @@ class QualityProcedure(NestedSet):
self.check_for_incorrect_child() self.check_for_incorrect_child()
def on_update(self): def on_update(self):
NestedSet.on_update(self)
self.set_parent() self.set_parent()
def after_insert(self): def after_insert(self):
self.set_parent() self.set_parent()
#if Child is Added through Tree View.
# add child to parent if missing
if self.parent_quality_procedure: if self.parent_quality_procedure:
parent_quality_procedure = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
parent_quality_procedure.append("processes", {"procedure": self.name}) if not [d for d in parent.processes if d.procedure == self.name]:
parent_quality_procedure.save() parent.append("processes", {"procedure": self.name, "process_description": self.name})
parent.save()
def on_trash(self): def on_trash(self):
if self.parent_quality_procedure: # clear from child table (sub procedures)
doc = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) frappe.db.sql('''update `tabQuality Procedure Process`
for process in doc.processes: set `procedure`='' where `procedure`=%s''', self.name)
if process.procedure == self.name: NestedSet.on_trash(self, allow_root_deletion=True)
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)
def set_parent(self): def set_parent(self):
rebuild_tree('Quality Procedure', 'parent_quality_procedure')
for process in self.processes: for process in self.processes:
# Set parent for only those children who don't have a parent # 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") has_parent = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
if not parent_quality_procedure and process.procedure: if not has_parent and process.procedure:
frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name)
def check_for_incorrect_child(self): def check_for_incorrect_child(self):
for process in self.processes: for process in self.processes:
if process.procedure: if process.procedure:
self.is_group = 1
# Check if any child process belongs to another parent. # Check if any child process belongs to another parent.
parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure") parent_quality_procedure = frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure")
if parent_quality_procedure and parent_quality_procedure != self.name: 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")) title=_("Invalid Child Procedure"))
self.is_group = 1
@frappe.whitelist() @frappe.whitelist()
def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
if parent is None or parent == "All Quality Procedures": if parent is None or parent == "All Quality Procedures":
parent = "" parent = ""
return frappe.db.sql(""" if parent:
select parent_procedure = frappe.get_doc('Quality Procedure', parent)
name as value, # return the list in order
is_group as expandable return [dict(
from value=d.procedure,
`tab{doctype}` expandable=frappe.db.get_value('Quality Procedure', d.procedure, 'is_group'))
where for d in parent_procedure.processes if d.procedure
ifnull(parent_quality_procedure, "")={parent} ]
""".format( else:
doctype = doctype, return frappe.get_all(doctype, fields=['name as value', 'is_group as expandable'],
parent=frappe.db.escape(parent) filters = dict(parent_quality_procedure = parent), order_by='name asc')
), as_dict=1)
@frappe.whitelist() @frappe.whitelist()
def add_node(): def add_node():
@@ -88,4 +77,4 @@ def add_node():
if args.parent_quality_procedure == 'All Quality Procedures': if args.parent_quality_procedure == 'All Quality Procedures':
args.parent_quality_procedure = None args.parent_quality_procedure = None
frappe.get_doc(args).insert() return frappe.get_doc(args).insert()

View File

@@ -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']
}
],
}

View File

@@ -15,7 +15,7 @@ frappe.treeview_settings["Quality Procedure"] = {
} }
}, },
], ],
breadcrumb: "Setup", breadcrumb: "Quality Management",
disable_add_node: true, disable_add_node: true,
root_label: "All Quality Procedures", root_label: "All Quality Procedures",
get_tree_root: false, get_tree_root: false,

View File

@@ -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()
]);
});

View File

@@ -6,54 +6,45 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest import unittest
class TestQualityProcedure(unittest.TestCase): from .quality_procedure import add_node
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()
self.assertEquals(test_create_procedure, test_get_procedure.get("name")) class TestQualityProcedure(unittest.TestCase):
self.assertEquals(test_create_nested_procedure, test_get_nested_procedure.get("name")) 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(): def create_procedure():
procedure = frappe.get_doc({ return frappe.get_doc(dict(
"doctype": "Quality Procedure", doctype = 'Quality Procedure',
"quality_procedure_name": "_Test Quality Procedure", quality_procedure_name = 'Test Procedure 1',
"processes": [ is_group = 1,
{ processes = [
"process_description": "_Test Quality Procedure Table", dict(process_description = 'Test Step 1')
}
] ]
}) )).insert()
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}

View File

@@ -10,6 +10,7 @@
], ],
"fields": [ "fields": [
{ {
"columns": 8,
"fieldname": "process_description", "fieldname": "process_description",
"fieldtype": "Text Editor", "fieldtype": "Text Editor",
"in_list_view": 1, "in_list_view": 1,
@@ -20,13 +21,14 @@
"fieldname": "procedure", "fieldname": "procedure",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Child Procedure", "label": "Sub Procedure",
"options": "Quality Procedure" "options": "Quality Procedure"
} }
], ],
"index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-06-17 15:44:38.937915", "modified": "2020-10-27 13:55:11.252945",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Procedure Process", "name": "Quality Procedure Process",

View File

@@ -2,9 +2,6 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on('Quality Review', { frappe.ui.form.on('Quality Review', {
onload: function(frm){
frm.set_value("date", frappe.datetime.get_today());
},
goal: function(frm) { goal: function(frm) {
frappe.call({ frappe.call({
"method": "frappe.client.get", "method": "frappe.client.get",

View File

@@ -1,6 +1,6 @@
{ {
"actions": [], "actions": [],
"autoname": "format:REV-{#####}", "autoname": "format:QA-REV-{#####}",
"creation": "2018-10-02 11:45:16.301955", "creation": "2018-10-02 11:45:16.301955",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -18,6 +18,7 @@
], ],
"fields": [ "fields": [
{ {
"default": "Today",
"fieldname": "date", "fieldname": "date",
"fieldtype": "Date", "fieldtype": "Date",
"in_list_view": 1, "in_list_view": 1,
@@ -50,7 +51,7 @@
"collapsible": 1, "collapsible": 1,
"fieldname": "sb_01", "fieldname": "sb_01",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Additional Information" "label": "Notes"
}, },
{ {
"fieldname": "reviews", "fieldname": "reviews",
@@ -63,7 +64,8 @@
"fieldname": "status", "fieldname": "status",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Status", "label": "Status",
"options": "Open\nClosed" "options": "Open\nPassed\nFailed",
"read_only": 1
}, },
{ {
"fieldname": "goal", "fieldname": "goal",
@@ -74,8 +76,15 @@
"reqd": 1 "reqd": 1
} }
], ],
"links": [], "index_web_pages_for_search": 1,
"modified": "2020-02-01 10:59:38.933115", "links": [
{
"group": "Review",
"link_doctype": "Quality Action",
"link_fieldname": "review"
}
],
"modified": "2020-10-21 12:56:47.046172",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Review", "name": "Quality Review",
@@ -120,5 +129,6 @@
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"title_field": "goal",
"track_changes": 1 "track_changes": 1
} }

View File

@@ -7,7 +7,26 @@ import frappe
from frappe.model.document import Document from frappe.model.document import Document
class QualityReview(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(): def review():
day = frappe.utils.getdate().day day = frappe.utils.getdate().day
@@ -24,7 +43,7 @@ def review():
elif goal.frequency == 'Monthly' and goal.date == str(day): elif goal.frequency == 'Monthly' and goal.date == str(day):
create_review(goal.name) 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) create_review(goal.name)
def create_review(goal): def create_review(goal):
@@ -36,15 +55,6 @@ def create_review(goal):
"date": frappe.utils.getdate() "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) review.insert(ignore_permissions=True)
def get_quarter(month): def get_quarter(month):

View File

@@ -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()
]);
});

View File

@@ -5,42 +5,18 @@ from __future__ import unicode_literals
import frappe import frappe
import unittest 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 ..quality_goal.test_quality_goal import get_quality_goal
from erpnext.quality_management.doctype.quality_goal.test_quality_goal import create_goal from .quality_review import review
class TestQualityReview(unittest.TestCase): class TestQualityReview(unittest.TestCase):
def test_review_creation(self):
quality_goal = get_quality_goal()
review()
def test_quality_review(self): # check if review exists
create_procedure() quality_review = frappe.get_doc('Quality Review', dict(goal = quality_goal.name))
create_unit() self.assertEqual(quality_goal.objectives[0].target, quality_review.reviews[0].target)
create_goal() quality_review.delete()
test_create_review = create_review()
test_get_review = get_review()
self.assertEquals(test_create_review, test_get_review)
def create_review(): quality_goal.delete()
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

View File

@@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2019-05-26 15:17:44.796958", "creation": "2019-05-26 15:17:44.796958",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@@ -9,10 +10,12 @@
"target", "target",
"uom", "uom",
"sb_00", "sb_00",
"status",
"review" "review"
], ],
"fields": [ "fields": [
{ {
"columns": 3,
"fieldname": "objective", "fieldname": "objective",
"fieldtype": "Text", "fieldtype": "Text",
"in_list_view": 1, "in_list_view": 1,
@@ -20,6 +23,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"columns": 2,
"fieldname": "target", "fieldname": "target",
"fieldtype": "Data", "fieldtype": "Data",
"in_list_view": 1, "in_list_view": 1,
@@ -27,6 +31,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"columns": 1,
"fetch_from": "target_unit", "fetch_from": "target_unit",
"fieldname": "uom", "fieldname": "uom",
"fieldtype": "Link", "fieldtype": "Link",
@@ -49,10 +54,20 @@
{ {
"fieldname": "cb_00", "fieldname": "cb_00",
"fieldtype": "Column Break" "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, "istable": 1,
"modified": "2019-05-26 16:14:12.586128", "links": [],
"modified": "2020-10-27 16:28:20.908637",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Quality Management", "module": "Quality Management",
"name": "Quality Review Objective", "name": "Quality Review Objective",

View File

@@ -139,7 +139,7 @@ def get_place_of_supply(party_details, doctype):
if not frappe.get_meta('Address').has_field('gst_state'): return if not frappe.get_meta('Address').has_field('gst_state'): return
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
address_name = party_details.shipping_address_name or party_details.customer_address address_name = party_details.customer_address or party_details.shipping_address_name
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"): elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
address_name = party_details.shipping_address or party_details.supplier_address address_name = party_details.shipping_address or party_details.supplier_address

View File

@@ -14,11 +14,9 @@ from six import string_types
def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""): def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
data = dict() data = dict()
result = [] result = []
warehouse, show_only_available_items = "", False
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock') allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
if not allow_negative_stock: warehouse, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
warehouse, show_only_available_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'show_only_available_items'])
if not frappe.db.exists('Item Group', item_group): if not frappe.db.exists('Item Group', item_group):
item_group = get_root_of('Item Group') item_group = get_root_of('Item Group')
@@ -48,7 +46,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt']) lft, rgt = frappe.db.get_value('Item Group', item_group, ['lft', 'rgt'])
bin_join_selection, bin_join_condition = "", "" bin_join_selection, bin_join_condition = "", ""
if show_only_available_items: if hide_unavailable_items:
bin_join_selection = ", `tabBin` bin" bin_join_selection = ", `tabBin` bin"
bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0" bin_join_condition = "AND bin.warehouse = %(warehouse)s AND bin.item_code = item.name AND bin.actual_qty > 0"
@@ -97,7 +95,7 @@ def get_items(start, page_length, price_list, item_group, pos_profile, search_va
for item in items_data: for item in items_data:
item_code = item.item_code item_code = item.item_code
item_price = item_prices.get(item_code) or {} item_price = item_prices.get(item_code) or {}
if not allow_negative_stock: if allow_negative_stock:
item_stock_qty = frappe.db.sql("""select ifnull(sum(actual_qty), 0) from `tabBin` where item_code = %s""", item_code)[0][0] item_stock_qty = frappe.db.sql("""select ifnull(sum(actual_qty), 0) from `tabBin` where item_code = %s""", item_code)[0][0]
else: else:
item_stock_qty = get_stock_availability(item_code, warehouse) item_stock_qty = get_stock_availability(item_code, warehouse)
@@ -231,13 +229,31 @@ def set_customer_info(fieldname, customer, value=""):
frappe.db.set_value('Customer', customer, 'loyalty_program', value) frappe.db.set_value('Customer', customer, 'loyalty_program', value)
contact = frappe.get_cached_value('Customer', customer, 'customer_primary_contact') contact = frappe.get_cached_value('Customer', customer, 'customer_primary_contact')
if not contact:
contact = frappe.db.sql("""
SELECT parent FROM `tabDynamic Link`
WHERE
parenttype = 'Contact' AND
parentfield = 'links' AND
link_doctype = 'Customer' AND
link_name = %s
""", (customer), as_dict=1)
contact = contact[0].get('parent') if contact else None
if contact: if not contact:
contact_doc = frappe.get_doc('Contact', contact) new_contact = frappe.new_doc('Contact')
if fieldname == 'email_id': new_contact.is_primary_contact = 1
contact_doc.set('email_ids', [{ 'email_id': value, 'is_primary': 1}]) new_contact.first_name = customer
frappe.db.set_value('Customer', customer, 'email_id', value) new_contact.set('links', [{'link_doctype': 'Customer', 'link_name': customer}])
elif fieldname == 'mobile_no': new_contact.save()
contact_doc.set('phone_nos', [{ 'phone': value, 'is_primary_mobile_no': 1}]) contact = new_contact.name
frappe.db.set_value('Customer', customer, 'mobile_no', value) frappe.db.set_value('Customer', customer, 'customer_primary_contact', contact)
contact_doc.save()
contact_doc = frappe.get_doc('Contact', contact)
if fieldname == 'email_id':
contact_doc.set('email_ids', [{ 'email_id': value, 'is_primary': 1}])
frappe.db.set_value('Customer', customer, 'email_id', value)
elif fieldname == 'mobile_no':
contact_doc.set('phone_nos', [{ 'phone': value, 'is_primary_mobile_no': 1}])
frappe.db.set_value('Customer', customer, 'mobile_no', value)
contact_doc.save()

View File

@@ -637,7 +637,7 @@ erpnext.PointOfSale.Controller = class {
if (!(available_qty > 0)) { if (!(available_qty > 0)) {
frappe.model.clear_doc(item_row.doctype, item_row.name); frappe.model.clear_doc(item_row.doctype, item_row.name);
frappe.throw({ frappe.throw({
title: _("Not Available"), title: __("Not Available"),
message: __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse]) message: __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse])
}) })
} else if (available_qty < qty_needed) { } else if (available_qty < qty_needed) {

View File

@@ -81,7 +81,7 @@ erpnext.PointOfSale.ItemSelector = class {
function get_item_image_html() { function get_item_image_html() {
if (item_image) { if (item_image) {
return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100"> return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
<img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;"> <img class="h-full" src="${item_image}" alt="${frappe.get_abbr(item.item_name)}" style="object-fit: cover;">
</div>` </div>`
} else { } else {
return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100"> return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">

View File

@@ -409,7 +409,7 @@ erpnext.PointOfSale.Payment = class {
${ ${
shortcuts.map(s => { shortcuts.map(s => {
return `<div class="shortcut rounded bg-light-grey text-dark-grey pt-2 pb-2 no-select pointer" data-value="${s}"> return `<div class="shortcut rounded bg-light-grey text-dark-grey pt-2 pb-2 no-select pointer" data-value="${s}">
${format_currency(s, currency)} ${format_currency(s, currency, 0)}
</div>` </div>`
}).join('') }).join('')
} }

View File

@@ -236,7 +236,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-09-12 16:11:31.910508", "modified": "2020-10-21 13:03:11.938072",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Quality Inspection", "name": "Quality Inspection",
@@ -257,7 +257,6 @@
"write": 1 "write": 1
} }
], ],
"quick_entry": 1,
"search_fields": "item_code, report_date, reference_name", "search_fields": "item_code, report_date, reference_name",
"show_name_in_global_search": 1, "show_name_in_global_search": 1,
"sort_field": "modified", "sort_field": "modified",

View File

@@ -615,6 +615,15 @@ class StockEntry(StockController):
if not row.subcontracted_item: if not row.subcontracted_item:
frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}") frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
.format(row.idx, frappe.bold(row.item_code))) .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): def validate_bom(self):
for d in self.get('items'): for d in self.get('items'):

View File

@@ -168,6 +168,7 @@ def get_stock_ledger_entries(filters, items):
from from
`tabStock Ledger Entry` sle force index (posting_sort_index) `tabStock Ledger Entry` sle force index (posting_sort_index)
where sle.docstatus < 2 %s %s where sle.docstatus < 2 %s %s
and is_cancelled = 0
order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec order by sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty""" % #nosec
(item_conditions_sql, conditions), as_dict=1) (item_conditions_sql, conditions), as_dict=1)

View File

@@ -288,7 +288,6 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
return return
convertible_cols = {} convertible_cols = {}
is_dict_obj = False is_dict_obj = False
if isinstance(result[0], dict): if isinstance(result[0], dict):
is_dict_obj = True is_dict_obj = True
@@ -310,13 +309,13 @@ def update_included_uom_in_report(columns, result, include_uom, conversion_facto
for row_idx, row in enumerate(result): for row_idx, row in enumerate(result):
data = row.items() if is_dict_obj else enumerate(row) data = row.items() if is_dict_obj else enumerate(row)
for key, value in data: for key, value in data:
if not key in convertible_columns or not conversion_factors[row_idx]: if key not in convertible_columns or not conversion_factors[row_idx-1]:
continue continue
if convertible_columns.get(key) == 'rate': if convertible_columns.get(key) == 'rate':
new_value = flt(value) * conversion_factors[row_idx] new_value = flt(value) * conversion_factors[row_idx-1]
else: else:
new_value = flt(value) / conversion_factors[row_idx] new_value = flt(value) / conversion_factors[row_idx-1]
if not is_dict_obj: if not is_dict_obj:
row.insert(key+1, new_value) row.insert(key+1, new_value)

View File

@@ -7,7 +7,7 @@ import json
from frappe import _ from frappe import _
from frappe import utils from frappe import utils
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import 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 datetime import datetime, timedelta
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.utils.user import is_website_user from frappe.utils.user import is_website_user
@@ -355,13 +355,13 @@ def set_service_level_agreement_variance(issue=None):
doc = frappe.get_doc("Issue", issue.name) doc = frappe.get_doc("Issue", issue.name)
if not doc.first_responded_on: # first_responded_on set when first reply is sent to customer 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) frappe.db.set_value(dt="Issue", dn=doc.name, field="response_by_variance", val=variance, update_modified=False)
if variance < 0: if variance < 0:
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False) 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 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) frappe.db.set_value(dt="Issue", dn=doc.name, field="resolution_by_variance", val=variance, update_modified=False)
if variance < 0: if variance < 0:
frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False) frappe.db.set_value(dt="Issue", dn=doc.name, field="agreement_status", val="Failed", update_modified=False)

View File

@@ -473,7 +473,6 @@ Cannot deduct when category is for 'Valuation' or 'Valuation and Total',Kan nie
Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',Kan nie aftrek as die kategorie vir &#39;Waardasie&#39; of &#39;Vaulering en Totaal&#39; is nie., Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',Kan nie aftrek as die kategorie vir &#39;Waardasie&#39; of &#39;Vaulering en Totaal&#39; is nie.,
"Cannot delete Serial No {0}, as it is used in stock transactions","Kan nie reeksnommer {0} uitvee nie, aangesien dit in voorraadtransaksies gebruik word", "Cannot delete Serial No {0}, as it is used in stock transactions","Kan nie reeksnommer {0} uitvee nie, aangesien dit in voorraadtransaksies gebruik word",
Cannot enroll more than {0} students for this student group.,Kan nie meer as {0} studente vir hierdie studente groep inskryf nie., Cannot enroll more than {0} students for this student group.,Kan nie meer as {0} studente vir hierdie studente groep inskryf nie.,
Cannot find Item with this barcode,Kan geen item met hierdie strepieskode vind nie,
Cannot find active Leave Period,Kan nie aktiewe verlofperiode vind nie, Cannot find active Leave Period,Kan nie aktiewe verlofperiode vind nie,
Cannot produce more Item {0} than Sales Order quantity {1},Kan nie meer item {0} produseer as hoeveelheid van die bestelling {1}, Cannot produce more Item {0} than Sales Order quantity {1},Kan nie meer item {0} produseer as hoeveelheid van die bestelling {1},
Cannot promote Employee with status Left,Kan nie werknemer bevorder met status links nie, Cannot promote Employee with status Left,Kan nie werknemer bevorder met status links nie,
@@ -690,7 +689,6 @@ Create Variants,Skep variante,
"Create and manage daily, weekly and monthly email digests.","Skep en bestuur daaglikse, weeklikse en maandelikse e-posverdelings.", "Create and manage daily, weekly and monthly email digests.","Skep en bestuur daaglikse, weeklikse en maandelikse e-posverdelings.",
Create customer quotes,Skep kliënte kwotasies, Create customer quotes,Skep kliënte kwotasies,
Create rules to restrict transactions based on values.,Skep reëls om transaksies gebaseer op waardes te beperk., Create rules to restrict transactions based on values.,Skep reëls om transaksies gebaseer op waardes te beperk.,
Created By,Gemaak deur,
Created {0} scorecards for {1} between: ,Geskep {0} telkaarte vir {1} tussen:, Created {0} scorecards for {1} between: ,Geskep {0} telkaarte vir {1} tussen:,
Creating Company and Importing Chart of Accounts,Skep &#39;n maatskappy en voer rekeningrekeninge in, Creating Company and Importing Chart of Accounts,Skep &#39;n maatskappy en voer rekeningrekeninge in,
Creating Fees,Fooie skep, Creating Fees,Fooie skep,
@@ -1078,7 +1076,6 @@ For Warehouse is required before Submit,Vir die pakhuis word vereis voor indieni
For row {0}: Enter Planned Qty,Vir ry {0}: Gee beplande hoeveelheid, For row {0}: Enter Planned Qty,Vir ry {0}: Gee beplande hoeveelheid,
"For {0}, only credit accounts can be linked against another debit entry",Vir {0} kan slegs kredietrekeninge gekoppel word teen &#39;n ander debietinskrywing, "For {0}, only credit accounts can be linked against another debit entry",Vir {0} kan slegs kredietrekeninge gekoppel word teen &#39;n ander debietinskrywing,
"For {0}, only debit accounts can be linked against another credit entry",Vir {0} kan slegs debietrekeninge gekoppel word teen &#39;n ander kredietinskrywing, "For {0}, only debit accounts can be linked against another credit entry",Vir {0} kan slegs debietrekeninge gekoppel word teen &#39;n ander kredietinskrywing,
Form View,Form View,
Forum Activity,Forum Aktiwiteit, Forum Activity,Forum Aktiwiteit,
Free item code is not selected,Gratis itemkode word nie gekies nie, Free item code is not selected,Gratis itemkode word nie gekies nie,
Freight and Forwarding Charges,Vrag en vragkoste, Freight and Forwarding Charges,Vrag en vragkoste,
@@ -2638,7 +2635,6 @@ Send SMS,Stuur SMS,
Send mass SMS to your contacts,Stuur massa-SMS na jou kontakte, Send mass SMS to your contacts,Stuur massa-SMS na jou kontakte,
Sensitivity,sensitiwiteit, Sensitivity,sensitiwiteit,
Sent,gestuur, Sent,gestuur,
Serial #,Serie #,
Serial No and Batch,Serial No and Batch, Serial No and Batch,Serial No and Batch,
Serial No is mandatory for Item {0},Volgnummer is verpligtend vir item {0}, Serial No is mandatory for Item {0},Volgnummer is verpligtend vir item {0},
Serial No {0} does not belong to Batch {1},Reeksnommer {0} hoort nie by bondel {1}, Serial No {0} does not belong to Batch {1},Reeksnommer {0} hoort nie by bondel {1},
@@ -3303,7 +3299,6 @@ Welcome to ERPNext,Welkom by ERPNext,
What do you need help with?,Waarmee het jy hulp nodig?, What do you need help with?,Waarmee het jy hulp nodig?,
What does it do?,Wat doen dit?, What does it do?,Wat doen dit?,
Where manufacturing operations are carried.,Waar vervaardigingsbedrywighede uitgevoer word., Where manufacturing operations are carried.,Waar vervaardigingsbedrywighede uitgevoer word.,
"While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA","Terwyl u rekening vir kindermaatskappy {0} skep, word ouerrekening {1} nie gevind nie. Skep asseblief die ouerrekening in die ooreenstemmende COA",
White,wit, White,wit,
Wire Transfer,Elektroniese oorbetaling, Wire Transfer,Elektroniese oorbetaling,
WooCommerce Products,WooCommerce Produkte, WooCommerce Products,WooCommerce Produkte,
@@ -3493,6 +3488,7 @@ Likes,Hou,
Merge with existing,Voeg saam met bestaande, Merge with existing,Voeg saam met bestaande,
Office,kantoor, Office,kantoor,
Orientation,geaardheid, Orientation,geaardheid,
Parent,Ouer,
Passive,passiewe, Passive,passiewe,
Payment Failed,Betaling misluk, Payment Failed,Betaling misluk,
Percent,persent, Percent,persent,
@@ -3543,6 +3539,7 @@ Shift,verskuiwing,
Show {0},Wys {0}, Show {0},Wys {0},
"Special Characters except ""-"", ""#"", ""."", ""/"", ""{"" and ""}"" not allowed in naming series","Spesiale karakters behalwe &quot;-&quot;, &quot;#&quot;, &quot;.&quot;, &quot;/&quot;, &quot;{&quot; En &quot;}&quot; word nie toegelaat in die naamreekse nie", "Special Characters except ""-"", ""#"", ""."", ""/"", ""{"" and ""}"" not allowed in naming series","Spesiale karakters behalwe &quot;-&quot;, &quot;#&quot;, &quot;.&quot;, &quot;/&quot;, &quot;{&quot; En &quot;}&quot; word nie toegelaat in die naamreekse nie",
Target Details,Teikenbesonderhede, Target Details,Teikenbesonderhede,
{0} already has a Parent Procedure {1}.,{0} het reeds &#39;n ouerprosedure {1}.,
API,API, API,API,
Annual,jaarlikse, Annual,jaarlikse,
Approved,goedgekeur, Approved,goedgekeur,
@@ -7558,10 +7555,6 @@ Quality Feedback Template Parameter,Parameter vir gehalte-terugvoersjabloon,
Quality Goal,Kwaliteit doel, Quality Goal,Kwaliteit doel,
Monitoring Frequency,Monitor frekwensie, Monitoring Frequency,Monitor frekwensie,
Weekday,weekdag, Weekday,weekdag,
January-April-July-October,Januarie-April-Julie-Oktober,
Revision and Revised On,Hersiening en hersien op,
Revision,hersiening,
Revised On,Hersien op,
Objectives,doelwitte, Objectives,doelwitte,
Quality Goal Objective,Kwaliteit Doelwit, Quality Goal Objective,Kwaliteit Doelwit,
Objective,Doel, Objective,Doel,
@@ -7574,7 +7567,6 @@ Parent Procedure,Ouerprosedure,
Processes,prosesse, Processes,prosesse,
Quality Procedure Process,Kwaliteit prosedure proses, Quality Procedure Process,Kwaliteit prosedure proses,
Process Description,Prosesbeskrywing, Process Description,Prosesbeskrywing,
Child Procedure,Kinderprosedure,
Link existing Quality Procedure.,Koppel die bestaande kwaliteitsprosedure., Link existing Quality Procedure.,Koppel die bestaande kwaliteitsprosedure.,
Additional Information,Bykomende inligting, Additional Information,Bykomende inligting,
Quality Review Objective,Doel van gehaltehersiening, Quality Review Objective,Doel van gehaltehersiening,
@@ -9091,7 +9083,6 @@ Unmarked days,Ongemerkte dae,
Absent Days,Afwesige dae, Absent Days,Afwesige dae,
Conditions and Formula variable and example,Voorwaardes en formule veranderlike en voorbeeld, Conditions and Formula variable and example,Voorwaardes en formule veranderlike en voorbeeld,
Feedback By,Terugvoer deur, Feedback By,Terugvoer deur,
MTNG-.YYYY.-.MM.-.DD.-,MTNG-.YYYY .-. MM .-. DD.-,
Manufacturing Section,Vervaardigingsafdeling, Manufacturing Section,Vervaardigingsafdeling,
"By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ",Die kliëntnaam word standaard ingestel volgens die volledige naam wat ingevoer is. As u wil hê dat klante deur &#39;n, "By default, the Customer Name is set as per the Full Name entered. If you want Customers to be named by a ",Die kliëntnaam word standaard ingestel volgens die volledige naam wat ingevoer is. As u wil hê dat klante deur &#39;n,
Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.,Stel die standaardpryslys op wanneer u &#39;n nuwe verkoopstransaksie skep. Itempryse word uit hierdie pryslys gehaal., Configure the default Price List when creating a new Sales transaction. Item prices will be fetched from this Price List.,Stel die standaardpryslys op wanneer u &#39;n nuwe verkoopstransaksie skep. Itempryse word uit hierdie pryslys gehaal.,
@@ -9692,7 +9683,6 @@ Available Balance,Beskikbare balans,
Reserved Balance,Gereserveerde balans, Reserved Balance,Gereserveerde balans,
Uncleared Balance,Onduidelike balans, Uncleared Balance,Onduidelike balans,
Payment related to {0} is not completed,Betaling wat verband hou met {0} is nie voltooi nie, Payment related to {0} is not completed,Betaling wat verband hou met {0} is nie voltooi nie,
Row #{}: Serial No{}. {} has already been transacted into another POS Invoice. Please select valid serial no.,Ry # {}: reeksnommer {}. {} is reeds oorgedra na &#39;n ander POS-faktuur. Kies &#39;n geldige reeksnr.,
Row #{}: Item Code: {} is not available under warehouse {}.,Ry # {}: Itemkode: {} is nie beskikbaar onder pakhuis {} nie., Row #{}: Item Code: {} is not available under warehouse {}.,Ry # {}: Itemkode: {} is nie beskikbaar onder pakhuis {} nie.,
Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.,Ry # {}: voorraadhoeveelheid nie genoeg vir artikelkode: {} onder pakhuis {}. Beskikbare hoeveelheid {}., Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}.,Ry # {}: voorraadhoeveelheid nie genoeg vir artikelkode: {} onder pakhuis {}. Beskikbare hoeveelheid {}.,
Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.,Ry # {}: kies &#39;n reeksnommer en &#39;n bondel teenoor item: {} of verwyder dit om die transaksie te voltooi., Row #{}: Please select a serial no and batch against item: {} or remove it to complete transaction.,Ry # {}: kies &#39;n reeksnommer en &#39;n bondel teenoor item: {} of verwyder dit om die transaksie te voltooi.,
@@ -9732,3 +9722,115 @@ Quantity not available for {0} in warehouse {1},Hoeveelheid nie beskikbaar vir {
Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.,Aktiveer asseblief Laat negatiewe voorraad toe in voorraadinstellings of skep voorraadinskrywing om voort te gaan., Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.,Aktiveer asseblief Laat negatiewe voorraad toe in voorraadinstellings of skep voorraadinskrywing om voort te gaan.,
No Inpatient Record found against patient {0},Geen pasiëntrekord gevind teen pasiënt nie {0}, No Inpatient Record found against patient {0},Geen pasiëntrekord gevind teen pasiënt nie {0},
An Inpatient Medication Order {0} against Patient Encounter {1} already exists.,Daar bestaan reeds &#39;n medikasiebevel vir binnepasiënte {0} teen pasiëntontmoeting {1}., An Inpatient Medication Order {0} against Patient Encounter {1} already exists.,Daar bestaan reeds &#39;n medikasiebevel vir binnepasiënte {0} teen pasiëntontmoeting {1}.,
Allow In Returns,Laat opbrengste toe,
Hide Unavailable Items,Versteek nie-beskikbare items,
Apply Discount on Discounted Rate,Pas afslag toe op afslag,
Therapy Plan Template,Terapieplan-sjabloon,
Fetching Template Details,Haal sjabloonbesonderhede op,
Linked Item Details,Gekoppelde itembesonderhede,
Therapy Types,Terapie tipes,
Therapy Plan Template Detail,Terapieplan sjabloonbesonderhede,
Non Conformance,Nie-ooreenstemming,
Process Owner,Proses eienaar,
Corrective Action,Korrektiewe aksie,
Preventive Action,Voorkomende aksie,
Problem,Probleem,
Responsible,Verantwoordelik,
Completion By,Voltooiing deur,
Process Owner Full Name,Proses eienaar se volle naam,
Right Index,Regte indeks,
Left Index,Linkse indeks,
Sub Procedure,Subprosedure,
Passed,Geslaag,
Print Receipt,Drukbewys,
Edit Receipt,Wysig kwitansie,
Focus on search input,Fokus op soekinsette,
Focus on Item Group filter,Fokus op Item Group filter,
Checkout Order / Submit Order / New Order,Afhandeling Bestelling / Dien Bestelling / Nuwe Bestelling in,
Add Order Discount,Bestel afslag byvoeg,
Item Code: {0} is not available under warehouse {1}.,Itemkode: {0} is nie beskikbaar onder pakhuis {1} nie.,
Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.,Reeksnommers nie beskikbaar vir item {0} onder pakhuis {1} nie. Probeer om die pakhuis te verander.,
Fetched only {0} available serial numbers.,Slegs {0} beskikbare reeksnommers gekry.,
Switch Between Payment Modes,Skakel tussen betaalmetodes,
Enter {0} amount.,Voer {0} bedrag in.,
You don't have enough points to redeem.,U het nie genoeg punte om af te los nie.,
You can redeem upto {0}.,U kan tot {0} gebruik.,
Enter amount to be redeemed.,Voer die bedrag in wat afgelos moet word.,
You cannot redeem more than {0}.,U kan nie meer as {0} gebruik nie.,
Open Form View,Maak vormaansig oop,
POS invoice {0} created succesfully,POS-faktuur {0} suksesvol geskep,
Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.,Voorraadhoeveelheid nie genoeg vir artikelkode: {0} onder pakhuis {1}. Beskikbare hoeveelheid {2}.,
Serial No: {0} has already been transacted into another POS Invoice.,Serienommer: {0} is reeds oorgedra na &#39;n ander POS-faktuur.,
Balance Serial No,Saldo Reeksnr,
Warehouse: {0} does not belong to {1},Pakhuis: {0} behoort nie tot {1},
Please select batches for batched item {0},Kies groepe vir &#39;n partytjie-item {0},
Please select quantity on row {0},Kies hoeveelheid in ry {0},
Please enter serial numbers for serialized item {0},Voer die reeksnommers in vir die reeks-item {0},
Batch {0} already selected.,Bondel {0} reeds gekies.,
Please select a warehouse to get available quantities,Kies &#39;n pakhuis om beskikbare hoeveelhede te kry,
"For transfer from source, selected quantity cannot be greater than available quantity",Vir oordrag vanaf die bron kan die gekose hoeveelheid nie groter wees as die beskikbare hoeveelheid nie,
Cannot find Item with this Barcode,Kan nie item met hierdie strepieskode vind nie,
{0} is mandatory. Maybe Currency Exchange record is not created for {1} to {2},{0} is verpligtend. Miskien word valuta-rekord nie vir {1} tot {2} geskep nie,
{} has submitted assets linked to it. You need to cancel the assets to create purchase return.,"{} het bates wat daaraan gekoppel is, ingedien. U moet die bates kanselleer om die aankoopopbrengs te skep.",
Cannot cancel this document as it is linked with submitted asset {0}. Please cancel it to continue.,"Kan nie hierdie dokument kanselleer nie, want dit is gekoppel aan die ingediende bate {0}. Kanselleer dit asseblief om voort te gaan.",
Row #{}: Serial No. {} has already been transacted into another POS Invoice. Please select valid serial no.,Ry # {}: Reeksnr. {} Is reeds oorgedra na &#39;n ander POS-faktuur. Kies &#39;n geldige reeksnr.,
Row #{}: Serial Nos. {} has already been transacted into another POS Invoice. Please select valid serial no.,Ry # {}: reeksnommers. {} Is reeds in &#39;n ander POS-faktuur oorgedra. Kies &#39;n geldige reeksnr.,
Item Unavailable,Item nie beskikbaar nie,
Row #{}: Serial No {} cannot be returned since it was not transacted in original invoice {},"Ry # {}: reeksnommer {} kan nie teruggestuur word nie, aangesien dit nie op die oorspronklike faktuur gedoen is nie {}",
Please set default Cash or Bank account in Mode of Payment {},Stel die verstek kontant- of bankrekening in die betaalmetode {},
Please set default Cash or Bank account in Mode of Payments {},Stel asseblief die standaard kontant- of bankrekening in die modus van betalings {},
Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account.,Maak seker dat die {} rekening &#39;n balansstaatrekening is. U kan die ouerrekening in &#39;n balansrekening verander of &#39;n ander rekening kies.,
Please ensure {} account is a Payable account. Change the account type to Payable or select a different account.,Maak seker dat die {} rekening &#39;n betaalbare rekening is. Verander die rekeningtipe na Betaalbaar of kies &#39;n ander rekening.,
Row {}: Expense Head changed to {} ,Ry {}: Onkostekop verander na {},
because account {} is not linked to warehouse {} ,omdat rekening {} nie aan pakhuis gekoppel is nie {},
or it is not the default inventory account,of dit is nie die standaardvoorraadrekening nie,
Expense Head Changed,Uitgawehoof verander,
because expense is booked against this account in Purchase Receipt {},omdat die onkoste teen hierdie rekening in die aankoopbewys {} bespreek word,
as no Purchase Receipt is created against Item {}. ,aangesien geen aankoopbewys teen item {} geskep word nie.,
This is done to handle accounting for cases when Purchase Receipt is created after Purchase Invoice,Dit word gedoen om rekeningkunde te hanteer vir gevalle waar aankoopbewys na aankoopfaktuur geskep word,
Purchase Order Required for item {},Bestelling benodig vir item {},
To submit the invoice without purchase order please set {} ,Stel die {} in om die faktuur sonder &#39;n bestelling in te dien,
as {} in {},soos in {},
Mandatory Purchase Order,Verpligte bestelling,
Purchase Receipt Required for item {},Aankoopbewys benodig vir item {},
To submit the invoice without purchase receipt please set {} ,Stel die {} in om die faktuur sonder aankoopbewys in te dien.,
Mandatory Purchase Receipt,Verpligte aankoopbewys,
POS Profile {} does not belongs to company {},POS-profiel {} behoort nie tot die maatskappy nie {},
User {} is disabled. Please select valid user/cashier,Gebruiker {} is gedeaktiveer. Kies &#39;n geldige gebruiker / kassier,
Row #{}: Original Invoice {} of return invoice {} is {}. ,Ry # {}: oorspronklike faktuur {} van retourfaktuur {} is {}.,
Original invoice should be consolidated before or along with the return invoice.,Die oorspronklike faktuur moet voor of saam met die retoervaktuur gekonsolideer word.,
You can add original invoice {} manually to proceed.,U kan oorspronklike fakture {} handmatig byvoeg om voort te gaan.,
Please ensure {} account is a Balance Sheet account. ,Maak seker dat die {} rekening &#39;n balansstaatrekening is.,
You can change the parent account to a Balance Sheet account or select a different account.,U kan die ouerrekening in &#39;n balansrekening verander of &#39;n ander rekening kies.,
Please ensure {} account is a Receivable account. ,Maak seker dat die {} rekening &#39;n ontvangbare rekening is.,
Change the account type to Receivable or select a different account.,Verander die rekeningtipe na Ontvangbaar of kies &#39;n ander rekening.,
{} can't be cancelled since the Loyalty Points earned has been redeemed. First cancel the {} No {},{} kan nie gekanselleer word nie omdat die verdienste van die Lojaliteitspunte afgelos is. Kanselleer eers die {} Nee {},
already exists,bestaan alreeds,
POS Closing Entry {} against {} between selected period,POS-sluitingsinskrywing {} teen {} tussen die gekose periode,
POS Invoice is {},POS-faktuur is {},
POS Profile doesn't matches {},POS-profiel stem nie ooreen nie {},
POS Invoice is not {},POS-faktuur is nie {},
POS Invoice isn't created by user {},POS-faktuur word nie deur gebruiker {} geskep nie,
Row #{}: {},Ry # {}: {},
Invalid POS Invoices,Ongeldige POS-fakture,
Please add the account to root level Company - {},Voeg die rekening by die maatskappy se wortelvlak - {},
"While creating account for Child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA","Terwyl u &#39;n rekening vir Child Company {0} skep, word die ouerrekening {1} nie gevind nie. Skep asseblief die ouerrekening in ooreenstemmende COA",
Account Not Found,Rekening nie gevind nie,
"While creating account for Child Company {0}, parent account {1} found as a ledger account.","Terwyl u &#39;n rekening vir Child Company {0} skep, word die ouerrekening {1} as &#39;n grootboekrekening gevind.",
Please convert the parent account in corresponding child company to a group account.,Skakel asseblief die ouerrekening in die ooreenstemmende kindermaatskappy om na &#39;n groeprekening.,
Invalid Parent Account,Ongeldige ouerrekening,
"Renaming it is only allowed via parent company {0}, to avoid mismatch.","Om dit te hernoem, is slegs toegelaat via moedermaatskappy {0}, om wanverhouding te voorkom.",
"If you {0} {1} quantities of the item {2}, the scheme {3} will be applied on the item.","As u {0} {1} hoeveelhede van die artikel {2} het, sal die skema {3} op die item toegepas word.",
"If you {0} {1} worth item {2}, the scheme {3} will be applied on the item.","As u {0} {1} die waarde van item {2} het, sal die skema {3} op die item toegepas word.",
"As the field {0} is enabled, the field {1} is mandatory.","Aangesien die veld {0} geaktiveer is, is die veld {1} verpligtend.",
"As the field {0} is enabled, the value of the field {1} should be more than 1.","Aangesien die veld {0} geaktiveer is, moet die waarde van die veld {1} meer as 1 wees.",
Cannot deliver Serial No {0} of item {1} as it is reserved to fullfill Sales Order {2},"Kan nie reeksnommer {0} van die artikel {1} lewer nie, aangesien dit gereserveer is vir die volledige bestelling {2}",
"Sales Order {0} has reservation for the item {1}, you can only deliver reserved {1} against {0}.",Verkooporder {0} het &#39;n bespreking vir die artikel {1}. U kan slegs gereserveerde {1} teen {0} aflewer.,
{0} Serial No {1} cannot be delivered,{0} Reeksnr. {1} kan nie afgelewer word nie,
Row {0}: Subcontracted Item is mandatory for the raw material {1},Ry {0}: Item uit die onderkontrak is verpligtend vir die grondstof {1},
"As there are sufficient raw materials, Material Request is not required for Warehouse {0}.","Aangesien daar voldoende grondstowwe is, is materiaalversoek nie nodig vir pakhuis {0} nie.",
" If you still want to proceed, please enable {0}.",Skakel {0} aan as u nog steeds wil voortgaan.,
The item referenced by {0} - {1} is already invoiced,Die item waarna verwys word deur {0} - {1} word reeds gefaktureer,
Therapy Session overlaps with {0},Terapiesessie oorvleuel met {0},
Therapy Sessions Overlapping,Terapiesessies oorvleuel,
Therapy Plans,Terapieplanne,
Can't render this file because it is too large.

Some files were not shown because too many files have changed in this diff Show More