Merge branch 'develop' into pr-dn-return
diff --git a/README.md b/README.md
index 0f6a521..15782a2 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@
<p>ERP made simple</p>
</p>
-[![Build Status](https://travis-ci.com/frappe/erpnext.svg)](https://travis-ci.com/frappe/erpnext)
+[![Build Status](https://api.travis-ci.com/frappe/erpnext.svg?branch=develop)](https://travis-ci.com/frappe/erpnext)
[![Open Source Helpers](https://www.codetriage.com/frappe/erpnext/badges/users.svg)](https://www.codetriage.com/frappe/erpnext)
[![Coverage Status](https://coveralls.io/repos/github/frappe/erpnext/badge.svg?branch=develop)](https://coveralls.io/github/frappe/erpnext?branch=develop)
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index b2a3f83..a18dbff 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -43,7 +43,7 @@
{
"hidden": 0,
"label": "Bank Statement",
- "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"dependencies\": [\n \"GL Entry\"\n ],\n \"doctype\": \"GL Entry\",\n \"is_query_report\": true,\n \"label\": \"Bank Reconciliation Statement\",\n \"name\": \"Bank Reconciliation Statement\",\n \"type\": \"report\"\n }\n]"
},
{
"hidden": 0,
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
index 2235298..f795dfa 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
@@ -94,8 +94,7 @@
callback: function(r) {
if(r.message===false) {
frm.set_value("company", "");
- frappe.throw(__(`Transactions against the company already exist!
- Chart Of accounts can be imported for company with no transactions`));
+ frappe.throw(__("Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions."));
} else {
frm.trigger("refresh");
}
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
index c92b58b..d79ad5f 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js
@@ -42,56 +42,56 @@
<tr><td>
<h4>
<i class="fa fa-hand-right"></i>
- ${__('Notes')}
+ {{__('Notes')}}
</h4>
<ul>
<li>
- ${__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")}
+ {{__("Pricing Rule is made to overwrite Price List / define discount percentage, based on some criteria.")}}
</li>
<li>
- ${__("If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")}
+ {{__("If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.")}}
</li>
<li>
- ${__('Discount Percentage can be applied either against a Price List or for all Price List.')}
+ {{__('Discount Percentage can be applied either against a Price List or for all Price List.')}}
</li>
<li>
- ${__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')}
+ {{__('To not apply Pricing Rule in a particular transaction, all applicable Pricing Rules should be disabled.')}}
</li>
</ul>
</td></tr>
<tr><td>
<h4><i class="fa fa-question-sign"></i>
- ${__('How Pricing Rule is applied?')}
+ {{__('How Pricing Rule is applied?')}}
</h4>
<ol>
<li>
- ${__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")}
+ {{__("Pricing Rule is first selected based on 'Apply On' field, which can be Item, Item Group or Brand.")}}
</li>
<li>
- ${__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.")}
+ {{__("Then Pricing Rules are filtered out based on Customer, Customer Group, Territory, Supplier, Supplier Type, Campaign, Sales Partner etc.")}}
</li>
<li>
- ${__('Pricing Rules are further filtered based on quantity.')}
+ {{__('Pricing Rules are further filtered based on quantity.')}}
</li>
<li>
- ${__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')}
+ {{__('If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.')}}
</li>
<li>
- ${__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')}
+ {{__('Even if there are multiple Pricing Rules with highest priority, then following internal priorities are applied:')}}
<ul>
<li>
- ${__('Item Code > Item Group > Brand')}
+ {{__('Item Code > Item Group > Brand')}}
</li>
<li>
- ${__('Customer > Customer Group > Territory')}
+ {{__('Customer > Customer Group > Territory')}}
</li>
<li>
- ${__('Supplier > Supplier Type')}
+ {{__('Supplier > Supplier Type')}}
</li>
</ul>
</li>
<li>
- ${__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')}
+ {{__('If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.')}}
</li>
</ol>
</td></tr>
diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
index a0b0cbb..ef77674 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py
@@ -7,6 +7,7 @@
import unittest
from frappe.utils import today
from erpnext.accounts.utils import get_fiscal_year
+from erpnext.buying.doctype.supplier.test_supplier import create_supplier
test_dependencies = ["Supplier Group"]
@@ -103,17 +104,20 @@
def test_single_threshold_tds_with_previous_vouchers_and_no_tds(self):
invoices = []
- frappe.db.set_value("Supplier", "Test TDS Supplier2", "tax_withholding_category", "Single Threshold TDS")
- pi = create_purchase_invoice(supplier="Test TDS Supplier2")
+ doc = create_supplier(supplier_name = "Test TDS Supplier ABC",
+ tax_withholding_category="Single Threshold TDS")
+ supplier = doc.name
+
+ pi = create_purchase_invoice(supplier=supplier)
pi.submit()
invoices.append(pi)
# TDS not applied
- pi = create_purchase_invoice(supplier="Test TDS Supplier2", do_not_apply_tds=True)
+ pi = create_purchase_invoice(supplier=supplier, do_not_apply_tds=True)
pi.submit()
invoices.append(pi)
- pi = create_purchase_invoice(supplier="Test TDS Supplier2")
+ pi = create_purchase_invoice(supplier=supplier)
pi.submit()
invoices.append(pi)
diff --git a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
index 9703527..6ae81d7 100644
--- a/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
+++ b/erpnext/accounts/page/bank_reconciliation/bank_reconciliation.js
@@ -156,7 +156,7 @@
setup_transactions_dom() {
const me = this;
- me.parent.$main_section.append(`<div class="transactions-table"></div>`)
+ me.parent.$main_section.append('<div class="transactions-table"></div>');
}
create_datatable() {
@@ -167,9 +167,7 @@
})
}
catch(err) {
- let msg = __(`Your file could not be processed by ERPNext.
- <br>It should be a standard CSV or XLSX file.
- <br>The headers should be in the first row.`)
+ let msg = __("Your file could not be processed. It should be a standard CSV or XLSX file with headers in the first row.");
frappe.throw(msg)
}
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 7ad164a..b2318a2 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -373,8 +373,8 @@
doctype_field = frappe.scrub(doctype)
frm.set_value(doctype_field, '');
frappe.msgprint({
- title: __(`Invalid ${doctype}`),
- message: __(`The selected ${doctype} doesn't contains selected Asset Item.`),
+ title: __('Invalid {0}', [__(doctype)]),
+ message: __('The selected {0} does not contain the selected Asset Item.', [__(doctype)]),
indicator: 'red'
});
}
@@ -436,7 +436,7 @@
depreciation_start_date: function(frm, cdt, cdn) {
const book = locals[cdt][cdn];
if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) {
- frappe.msgprint(__(`Depreciation Posting Date should not be equal to Available for Use Date.`));
+ frappe.msgprint(__("Depreciation Posting Date should not be equal to Available for Use Date."));
book.depreciation_start_date = "";
frm.refresh_field("finance_books");
}
diff --git a/erpnext/buying/doctype/supplier/test_supplier.py b/erpnext/buying/doctype/supplier/test_supplier.py
index a377ec9..f9c8d35 100644
--- a/erpnext/buying/doctype/supplier/test_supplier.py
+++ b/erpnext/buying/doctype/supplier/test_supplier.py
@@ -120,3 +120,20 @@
# Rollback
address.delete()
+
+def create_supplier(**args):
+ args = frappe._dict(args)
+
+ try:
+ doc = frappe.get_doc({
+ "doctype": "Supplier",
+ "supplier_name": args.supplier_name,
+ "supplier_group": args.supplier_group or "Services",
+ "supplier_type": args.supplier_type or "Company",
+ "tax_withholding_category": args.tax_withholding_category
+ }).insert()
+
+ return doc
+
+ except frappe.DuplicateEntryError:
+ return frappe.get_doc("Supplier", args.supplier_name)
\ No newline at end of file
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 7504746..515239a 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -416,26 +416,26 @@
return
for d in self.get('items'):
- if self.doctype == "Sales Invoice":
- e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
- f = [d.item_code, d.description, d.sales_order or d.delivery_note]
+ if self.doctype in ["POS Invoice","Sales Invoice"]:
+ stock_items = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
+ non_stock_items = [d.item_code, d.description, d.sales_order or d.delivery_note]
elif self.doctype == "Delivery Note":
- e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
- f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
+ stock_items = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
+ non_stock_items = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
elif self.doctype in ["Sales Order", "Quotation"]:
- e = [d.item_code, d.description, d.warehouse, '']
- f = [d.item_code, d.description]
+ stock_items = [d.item_code, d.description, d.warehouse, '']
+ non_stock_items = [d.item_code, d.description]
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
- if e in check_list:
+ if stock_items in check_list:
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
- check_list.append(e)
+ check_list.append(stock_items)
else:
- if f in chk_dupl_itm:
+ if non_stock_items in chk_dupl_itm:
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
- chk_dupl_itm.append(f)
+ chk_dupl_itm.append(non_stock_items)
def validate_target_warehouse(self):
items = self.get("items") + (self.get("packed_items") or [])
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index 4436ab0..2f7b361 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -229,9 +229,9 @@
def check_expense_account(self, item):
if not item.get("expense_account"):
- frappe.throw(_("Row #{0}: Expense Account not set for Item {1}. Please set an Expense Account in the Items table")
- .format(item.idx, frappe.bold(item.item_code)),
- title=_("Expense Account Missing"))
+ msg = _("Please set an Expense Account in the Items table")
+ frappe.throw(_("Row #{0}: Expense Account not set for the Item {1}. {2}")
+ .format(item.idx, frappe.bold(item.item_code), msg), title=_("Expense Account Missing"))
else:
is_expense_account = frappe.db.get_value("Account",
@@ -247,7 +247,9 @@
for d in self.items:
if not d.batch_no: continue
- serial_nos = [sr.name for sr in frappe.get_all("Serial No", {'batch_no': d.batch_no})]
+ serial_nos = [sr.name for sr in frappe.get_all("Serial No",
+ {'batch_no': d.batch_no, 'status': 'Inactive'})]
+
if serial_nos:
frappe.db.set_value("Serial No", { 'name': ['in', serial_nos] }, "batch_no", None)
diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
index 99b8214..dc3ae8b 100644
--- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
+++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js
@@ -4,7 +4,7 @@
let from_time = Date.parse('01/01/2019 ' + d.from_time);
let to_time = Date.parse('01/01/2019 ' + d.to_time);
if (from_time > to_time) {
- frappe.throw(__(`In row ${i + 1} of Appointment Booking Slots : "To Time" must be later than "From Time"`));
+ frappe.throw(__('In row {0} of Appointment Booking Slots: "To Time" must be later than "From Time".', [i + 1]));
}
});
}
\ No newline at end of file
diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py
index 8aa7453..efbaa71 100644
--- a/erpnext/erpnext_integrations/connectors/shopify_connection.py
+++ b/erpnext/erpnext_integrations/connectors/shopify_connection.py
@@ -149,26 +149,28 @@
si.shopify_order_number = shopify_order.get("name")
si.set_posting_time = 1
si.posting_date = posting_date
+ si.due_date = posting_date
si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-"
si.flags.ignore_mandatory = True
set_cost_center(si.items, shopify_settings.cost_center)
si.insert(ignore_mandatory=True)
si.submit()
- make_payament_entry_against_sales_invoice(si, shopify_settings)
+ make_payament_entry_against_sales_invoice(si, shopify_settings, posting_date)
frappe.db.commit()
def set_cost_center(items, cost_center):
for item in items:
item.cost_center = cost_center
-def make_payament_entry_against_sales_invoice(doc, shopify_settings):
+def make_payament_entry_against_sales_invoice(doc, shopify_settings, posting_date=None):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
- payemnt_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account)
- payemnt_entry.flags.ignore_mandatory = True
- payemnt_entry.reference_no = doc.name
- payemnt_entry.reference_date = nowdate()
- payemnt_entry.insert(ignore_permissions=True)
- payemnt_entry.submit()
+ payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account)
+ payment_entry.flags.ignore_mandatory = True
+ payment_entry.reference_no = doc.name
+ payment_entry.posting_date = posting_date or nowdate()
+ payment_entry.reference_date = posting_date or nowdate()
+ payment_entry.insert(ignore_permissions=True)
+ payment_entry.submit()
def create_delivery_note(shopify_order, shopify_settings, so):
if not cint(shopify_settings.sync_delivery_note):
diff --git a/erpnext/healthcare/desk_page/healthcare/healthcare.json b/erpnext/healthcare/desk_page/healthcare/healthcare.json
index 6546b08..81d6048 100644
--- a/erpnext/healthcare/desk_page/healthcare/healthcare.json
+++ b/erpnext/healthcare/desk_page/healthcare/healthcare.json
@@ -43,7 +43,7 @@
{
"hidden": 0,
"label": "Reports",
- "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t}\n]"
+ "links": "[\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Patient Appointment Analytics\",\n\t\t\"doctype\": \"Patient Appointment\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Lab Test Report\",\n\t\t\"doctype\": \"Lab Test\",\n\t\t\"label\": \"Lab Test Report\"\n\t},\n\t{\n\t\t\"type\": \"report\",\n\t\t\"is_query_report\": true,\n\t\t\"name\": \"Inpatient Medication Orders\",\n\t\t\"doctype\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Orders\"\n\t}\n]"
}
],
"category": "Domains",
@@ -64,7 +64,7 @@
"idx": 0,
"is_standard": 1,
"label": "Healthcare",
- "modified": "2020-06-25 23:50:56.951698",
+ "modified": "2020-11-23 23:00:48.764377",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare",
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
index 23e7519..5dac23a 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
@@ -274,4 +274,6 @@
def get_current_healthcare_service_unit(inpatient_record):
ip_record = frappe.get_doc('Inpatient Record', inpatient_record)
- return ip_record.inpatient_occupancies[-1].service_unit
\ No newline at end of file
+ if ip_record.inpatient_occupancies:
+ return ip_record.inpatient_occupancies[-1].service_unit
+ return
\ No newline at end of file
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/__init__.py b/erpnext/healthcare/report/inpatient_medication_orders/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/erpnext/healthcare/report/inpatient_medication_orders/__init__.py
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js
new file mode 100644
index 0000000..a10f837
--- /dev/null
+++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.js
@@ -0,0 +1,57 @@
+// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+/* eslint-disable */
+
+frappe.query_reports["Inpatient Medication Orders"] = {
+ "filters": [
+ {
+ fieldname: "company",
+ label: __("Company"),
+ fieldtype: "Link",
+ options: "Company",
+ default: frappe.defaults.get_user_default("Company"),
+ reqd: 1
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ reqd: 1
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.now_date(),
+ reqd: 1
+ },
+ {
+ fieldname: "patient",
+ label: __("Patient"),
+ fieldtype: "Link",
+ options: "Patient"
+ },
+ {
+ fieldname: "service_unit",
+ label: __("Healthcare Service Unit"),
+ fieldtype: "Link",
+ options: "Healthcare Service Unit",
+ get_query: () => {
+ var company = frappe.query_report.get_filter_value('company');
+ return {
+ filters: {
+ 'company': company,
+ 'is_group': 0
+ }
+ }
+ }
+ },
+ {
+ fieldname: "show_completed_orders",
+ label: __("Show Completed Orders"),
+ fieldtype: "Check",
+ default: 1
+ }
+ ]
+};
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json
new file mode 100644
index 0000000..9217fa1
--- /dev/null
+++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.json
@@ -0,0 +1,36 @@
+{
+ "add_total_row": 0,
+ "columns": [],
+ "creation": "2020-11-23 17:25:58.802949",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "filters": [],
+ "idx": 0,
+ "is_standard": "Yes",
+ "json": "{}",
+ "modified": "2020-11-23 19:40:20.227591",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Inpatient Medication Orders",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Inpatient Medication Order",
+ "report_name": "Inpatient Medication Orders",
+ "report_type": "Script Report",
+ "roles": [
+ {
+ "role": "System Manager"
+ },
+ {
+ "role": "Healthcare Administrator"
+ },
+ {
+ "role": "Nursing User"
+ },
+ {
+ "role": "Physician"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py
new file mode 100644
index 0000000..b907730
--- /dev/null
+++ b/erpnext/healthcare/report/inpatient_medication_orders/inpatient_medication_orders.py
@@ -0,0 +1,198 @@
+# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_current_healthcare_service_unit
+
+def execute(filters=None):
+ columns = get_columns()
+ data = get_data(filters)
+ chart = get_chart_data(data)
+
+ return columns, data, None, chart
+
+def get_columns():
+ return [
+ {
+ "fieldname": "patient",
+ "fieldtype": "Link",
+ "label": "Patient",
+ "options": "Patient",
+ "width": 200
+ },
+ {
+ "fieldname": "healthcare_service_unit",
+ "fieldtype": "Link",
+ "label": "Healthcare Service Unit",
+ "options": "Healthcare Service Unit",
+ "width": 150
+ },
+ {
+ "fieldname": "drug",
+ "fieldtype": "Link",
+ "label": "Drug Code",
+ "options": "Item",
+ "width": 150
+ },
+ {
+ "fieldname": "drug_name",
+ "fieldtype": "Data",
+ "label": "Drug Name",
+ "width": 150
+ },
+ {
+ "fieldname": "dosage",
+ "fieldtype": "Link",
+ "label": "Dosage",
+ "options": "Prescription Dosage",
+ "width": 80
+ },
+ {
+ "fieldname": "dosage_form",
+ "fieldtype": "Link",
+ "label": "Dosage Form",
+ "options": "Dosage Form",
+ "width": 100
+ },
+ {
+ "fieldname": "date",
+ "fieldtype": "Date",
+ "label": "Date",
+ "width": 100
+ },
+ {
+ "fieldname": "time",
+ "fieldtype": "Time",
+ "label": "Time",
+ "width": 100
+ },
+ {
+ "fieldname": "is_completed",
+ "fieldtype": "Check",
+ "label": "Is Order Completed",
+ "width": 100
+ },
+ {
+ "fieldname": "healthcare_practitioner",
+ "fieldtype": "Link",
+ "label": "Healthcare Practitioner",
+ "options": "Healthcare Practitioner",
+ "width": 200
+ },
+ {
+ "fieldname": "inpatient_medication_entry",
+ "fieldtype": "Link",
+ "label": "Inpatient Medication Entry",
+ "options": "Inpatient Medication Entry",
+ "width": 200
+ },
+ {
+ "fieldname": "inpatient_record",
+ "fieldtype": "Link",
+ "label": "Inpatient Record",
+ "options": "Inpatient Record",
+ "width": 200
+ }
+ ]
+
+def get_data(filters):
+ conditions, values = get_conditions(filters)
+
+ data = frappe.db.sql("""
+ SELECT
+ parent.patient, parent.inpatient_record, parent.practitioner,
+ child.drug, child.drug_name, child.dosage, child.dosage_form,
+ child.date, child.time, child.is_completed, child.name
+ FROM `tabInpatient Medication Order` parent
+ INNER JOIN `tabInpatient Medication Order Entry` child
+ ON child.parent = parent.name
+ WHERE
+ parent.docstatus = 1
+ {conditions}
+ ORDER BY date, time
+ """.format(conditions=conditions), values, as_dict=1)
+
+ data = get_inpatient_details(data, filters.get("service_unit"))
+
+ return data
+
+def get_conditions(filters):
+ conditions = ""
+ values = dict()
+
+ if filters.get("company"):
+ conditions += " AND parent.company = %(company)s"
+ values["company"] = filters.get("company")
+
+ if filters.get("from_date") and filters.get("to_date"):
+ conditions += " AND child.date BETWEEN %(from_date)s and %(to_date)s"
+ values["from_date"] = filters.get("from_date")
+ values["to_date"] = filters.get("to_date")
+
+ if filters.get("patient"):
+ conditions += " AND parent.patient = %(patient)s"
+ values["patient"] = filters.get("patient")
+
+ if not filters.get("show_completed_orders"):
+ conditions += " AND child.is_completed = 0"
+
+ return conditions, values
+
+
+def get_inpatient_details(data, service_unit):
+ service_unit_filtered_data = []
+
+ for entry in data:
+ entry["healthcare_service_unit"] = get_current_healthcare_service_unit(entry.inpatient_record)
+ if entry.is_completed:
+ entry["inpatient_medication_entry"] = get_inpatient_medication_entry(entry.name)
+
+ if service_unit and entry.healthcare_service_unit and service_unit != entry.healthcare_service_unit:
+ service_unit_filtered_data.append(entry)
+
+ entry.pop("name", None)
+
+ for entry in service_unit_filtered_data:
+ data.remove(entry)
+
+ return data
+
+def get_inpatient_medication_entry(order_entry):
+ return frappe.db.get_value("Inpatient Medication Entry Detail", {"against_imoe": order_entry}, "parent")
+
+def get_chart_data(data):
+ if not data:
+ return None
+
+ labels = ["Pending", "Completed"]
+ datasets = []
+
+ status_wise_data = {
+ "Pending": 0,
+ "Completed": 0
+ }
+
+ for d in data:
+ if d.is_completed:
+ status_wise_data["Completed"] += 1
+ else:
+ status_wise_data["Pending"] += 1
+
+ datasets.append({
+ "name": "Inpatient Medication Order Status",
+ "values": [status_wise_data.get("Pending"), status_wise_data.get("Completed")]
+ })
+
+ chart = {
+ "data": {
+ "labels": labels,
+ "datasets": datasets
+ },
+ "type": "donut",
+ "height": 300
+ }
+
+ chart["fieldtype"] = "Data"
+
+ return chart
\ No newline at end of file
diff --git a/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py b/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py
new file mode 100644
index 0000000..0d3f45f
--- /dev/null
+++ b/erpnext/healthcare/report/inpatient_medication_orders/test_inpatient_medication_orders.py
@@ -0,0 +1,128 @@
+# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import unittest
+import frappe
+import datetime
+from frappe.utils import getdate, now_datetime
+from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
+from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
+from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
+from erpnext.healthcare.report.inpatient_medication_orders.inpatient_medication_orders import execute
+
+class TestInpatientMedicationOrders(unittest.TestCase):
+ @classmethod
+ def setUpClass(self):
+ frappe.db.sql("delete from `tabInpatient Medication Order` where company='_Test Company'")
+ frappe.db.sql("delete from `tabInpatient Medication Entry` where company='_Test Company'")
+ self.patient = create_patient()
+ self.ip_record = create_records(self.patient)
+
+ def test_inpatient_medication_orders_report(self):
+ filters = {
+ 'company': '_Test Company',
+ 'from_date': getdate(),
+ 'to_date': getdate(),
+ 'patient': '_Test IPD Patient',
+ 'service_unit': 'Test Service Unit Ip Occupancy - _TC'
+ }
+
+ report = execute(filters)
+
+ expected_data = [
+ {
+ 'patient': '_Test IPD Patient',
+ 'inpatient_record': self.ip_record.name,
+ 'practitioner': None,
+ 'drug': 'Dextromethorphan',
+ 'drug_name': 'Dextromethorphan',
+ 'dosage': 1.0,
+ 'dosage_form': 'Tablet',
+ 'date': getdate(),
+ 'time': datetime.timedelta(seconds=32400),
+ 'is_completed': 0,
+ 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
+ },
+ {
+ 'patient': '_Test IPD Patient',
+ 'inpatient_record': self.ip_record.name,
+ 'practitioner': None,
+ 'drug': 'Dextromethorphan',
+ 'drug_name': 'Dextromethorphan',
+ 'dosage': 1.0,
+ 'dosage_form': 'Tablet',
+ 'date': getdate(),
+ 'time': datetime.timedelta(seconds=50400),
+ 'is_completed': 0,
+ 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
+ },
+ {
+ 'patient': '_Test IPD Patient',
+ 'inpatient_record': self.ip_record.name,
+ 'practitioner': None,
+ 'drug': 'Dextromethorphan',
+ 'drug_name': 'Dextromethorphan',
+ 'dosage': 1.0,
+ 'dosage_form': 'Tablet',
+ 'date': getdate(),
+ 'time': datetime.timedelta(seconds=75600),
+ 'is_completed': 0,
+ 'healthcare_service_unit': 'Test Service Unit Ip Occupancy - _TC'
+ }
+ ]
+
+ self.assertEqual(expected_data, report[1])
+
+ filters = frappe._dict(from_date=getdate(), to_date=getdate(), from_time='', to_time='')
+ ipme = create_ipme(filters)
+ ipme.submit()
+
+ filters = {
+ 'company': '_Test Company',
+ 'from_date': getdate(),
+ 'to_date': getdate(),
+ 'patient': '_Test IPD Patient',
+ 'service_unit': 'Test Service Unit Ip Occupancy - _TC',
+ 'show_completed_orders': 0
+ }
+
+ report = execute(filters)
+ self.assertEqual(len(report[1]), 0)
+
+ def tearDown(self):
+ if frappe.db.get_value('Patient', self.patient, 'inpatient_record'):
+ # cleanup - Discharge
+ schedule_discharge(frappe.as_json({'patient': self.patient}))
+ self.ip_record.reload()
+ mark_invoiced_inpatient_occupancy(self.ip_record)
+
+ self.ip_record.reload()
+ discharge_patient(self.ip_record)
+
+ for entry in frappe.get_all('Inpatient Medication Entry'):
+ doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
+ doc.cancel()
+ doc.delete()
+
+ for entry in frappe.get_all('Inpatient Medication Order'):
+ doc = frappe.get_doc('Inpatient Medication Order', entry.name)
+ doc.cancel()
+ doc.delete()
+
+
+def create_records(patient):
+ frappe.db.sql("""delete from `tabInpatient Record`""")
+
+ # Admit
+ ip_record = create_inpatient(patient)
+ ip_record.expected_length_of_stay = 0
+ ip_record.save()
+ ip_record.reload()
+ service_unit = get_healthcare_service_unit()
+ admit_patient(ip_record, service_unit, now_datetime())
+
+ ipmo = create_ipmo(patient)
+ ipmo.submit()
+
+ return ip_record
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index b4c57d7..741176f 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -237,6 +237,9 @@
"Website Settings": {
"validate": "erpnext.portal.doctype.products_settings.products_settings.home_page_is_products"
},
+ "Tax Category": {
+ "validate": "erpnext.regional.india.utils.validate_tax_category"
+ },
"Sales Invoice": {
"on_submit": [
"erpnext.regional.create_transaction_log",
diff --git a/erpnext/hr/doctype/department_approver/department_approver.py b/erpnext/hr/doctype/department_approver/department_approver.py
index 9b2de0e..d337959 100644
--- a/erpnext/hr/doctype/department_approver/department_approver.py
+++ b/erpnext/hr/doctype/department_approver/department_approver.py
@@ -20,7 +20,7 @@
approvers = []
department_details = {}
department_list = []
- employee = frappe.get_value("Employee", filters.get("employee"), ["department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True)
+ employee = frappe.get_value("Employee", filters.get("employee"), ["employee_name","department", "leave_approver", "expense_approver", "shift_request_approver"], as_dict=True)
employee_department = filters.get("department") or employee.department
if employee_department:
@@ -59,11 +59,9 @@
and approver.approver=user.name""",(d, "%" + txt + "%", parentfield), as_list=True)
if len(approvers) == 0:
- frappe.throw(_("Please set {0} for the Employee or for Department: {1}").
- format(
- field_name, frappe.bold(employee_department),
- frappe.bold(employee.name)
- ),
- title=_(field_name + " Missing"))
+ error_msg = _("Please set {0} for the Employee: {1}").format(field_name, frappe.bold(employee.employee_name))
+ if department_list:
+ error_msg += _(" or for Department: {0}").format(frappe.bold(employee_department))
+ frappe.throw(error_msg, title=_(field_name + " Missing"))
return set(tuple(approver) for approver in approvers)
diff --git a/erpnext/loan_management/doctype/loan/loan.json b/erpnext/loan_management/doctype/loan/loan.json
index e8ecf01..d468f52 100644
--- a/erpnext/loan_management/doctype/loan/loan.json
+++ b/erpnext/loan_management/doctype/loan/loan.json
@@ -332,6 +332,7 @@
"read_only": 1
},
{
+ "depends_on": "eval:doc.is_secured_loan",
"fetch_from": "loan_application.maximum_loan_amount",
"fieldname": "maximum_loan_amount",
"fieldtype": "Currency",
@@ -352,7 +353,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-11-05 10:04:00.762975",
+ "modified": "2020-11-24 12:27:23.208240",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan",
diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
index 233862b..f341e81 100644
--- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
+++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py
@@ -171,10 +171,10 @@
return security_value
@frappe.whitelist()
-def get_disbursal_amount(loan):
- loan_details = frappe.get_all("Loan", fields = ["loan_amount", "disbursed_amount", "total_payment",
- "total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan"],
- filters= { "name": loan })[0]
+def get_disbursal_amount(loan, on_current_security_price=0):
+ loan_details = frappe.get_value("Loan", loan, ["loan_amount", "disbursed_amount", "total_payment",
+ "total_principal_paid", "total_interest_payable", "status", "is_term_loan", "is_secured_loan",
+ "maximum_loan_amount"], as_dict=1)
if loan_details.is_secured_loan and frappe.get_all('Loan Security Shortfall', filters={'loan': loan,
'status': 'Pending'}):
@@ -188,9 +188,12 @@
- flt(loan_details.total_principal_paid)
security_value = 0.0
- if loan_details.is_secured_loan:
+ if loan_details.is_secured_loan and on_current_security_price:
security_value = get_total_pledged_security_value(loan)
+ if loan_details.is_secured_loan and not on_current_security_price:
+ security_value = flt(loan_details.maximum_loan_amount)
+
if not security_value and not loan_details.is_secured_loan:
security_value = flt(loan_details.loan_amount)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js
index b051b32..4e8dd41 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.js
+++ b/erpnext/manufacturing/doctype/job_card/job_card.js
@@ -31,6 +31,16 @@
}
}
+ frm.set_query("quality_inspection", function() {
+ return {
+ query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query",
+ filters: {
+ "item_code": frm.doc.production_item,
+ "reference_name": frm.doc.name
+ }
+ };
+ });
+
frm.trigger("toggle_operation_number");
if (frm.doc.docstatus == 0 && (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity)
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.json b/erpnext/manufacturing/doctype/job_card/job_card.json
index 575e719..5713f69 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.json
+++ b/erpnext/manufacturing/doctype/job_card/job_card.json
@@ -20,6 +20,7 @@
"production_item",
"item_name",
"for_quantity",
+ "quality_inspection",
"wip_warehouse",
"column_break_12",
"employee",
@@ -305,11 +306,19 @@
"label": "Sequence Id",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "depends_on": "eval:!doc.__islocal;",
+ "fieldname": "quality_inspection",
+ "fieldtype": "Link",
+ "label": "Quality Inspection",
+ "no_copy": 1,
+ "options": "Quality Inspection"
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-10-14 12:58:25.327897",
+ "modified": "2020-11-19 18:26:50.531664",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Job Card",
diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py
index 4dfa78b..d15d81e 100644
--- a/erpnext/manufacturing/doctype/job_card/job_card.py
+++ b/erpnext/manufacturing/doctype/job_card/job_card.py
@@ -353,17 +353,19 @@
@frappe.whitelist()
def get_operations(doctype, txt, searchfield, start, page_len, filters):
- if filters.get("work_order"):
- args = {"parent": filters.get("work_order")}
- if txt:
- args["operation"] = ("like", "%{0}%".format(txt))
+ if not filters.get("work_order"):
+ frappe.msgprint(_("Please select a Work Order first."))
+ return []
+ args = {"parent": filters.get("work_order")}
+ if txt:
+ args["operation"] = ("like", "%{0}%".format(txt))
- return frappe.get_all("Work Order Operation",
- filters = args,
- fields = ["distinct operation as operation"],
- limit_start = start,
- limit_page_length = page_len,
- order_by="idx asc", as_list=1)
+ return frappe.get_all("Work Order Operation",
+ filters = args,
+ fields = ["distinct operation as operation"],
+ limit_start = start,
+ limit_page_length = page_len,
+ order_by="idx asc", as_list=1)
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):
diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
index 2ac6fa0..8cd0164 100644
--- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
+++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js
@@ -25,11 +25,11 @@
],
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
- if (column.id == "Item"){
- if (data["Enough Parts to Build"] > 0){
- value = `<a style='color:green' href="#Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
+ if (column.id == "item") {
+ if (data["enough_parts_to_build"] > 0) {
+ value = `<a style='color:green' href="#Form/Item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
} else {
- value = `<a style='color:red' href="#Form/Item/${data['Item']}" data-doctype="Item">${data['Item']}</a>`
+ value = `<a style='color:red' href="#Form/Item/${data['item']}" data-doctype="Item">${data['item']}</a>`;
}
}
return value
diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js
index 8c6a9cf..002ddb2 100644
--- a/erpnext/projects/doctype/task/task.js
+++ b/erpnext/projects/doctype/task/task.js
@@ -49,7 +49,10 @@
},
callback: function (r) {
if (r.message.length > 0) {
- frappe.msgprint(__(`Cannot convert it to non-group. The following child Tasks exist: ${r.message.join(", ")}.`));
+ let message = __('Cannot convert Task to non-group because the following child Tasks exist: {0}.',
+ [r.message.join(", ")]
+ );
+ frappe.msgprint(message);
frm.reload_doc();
}
}
diff --git a/erpnext/public/js/hub/pages/Category.vue b/erpnext/public/js/hub/pages/Category.vue
index 057fe8b..16d0601 100644
--- a/erpnext/public/js/hub/pages/Category.vue
+++ b/erpnext/public/js/hub/pages/Category.vue
@@ -32,7 +32,7 @@
item_id_fieldname: 'name',
// Constants
- empty_state_message: __(`No items in this category yet.`),
+ empty_state_message: __('No items in this category yet.'),
search_value: '',
diff --git a/erpnext/public/js/hub/pages/FeaturedItems.vue b/erpnext/public/js/hub/pages/FeaturedItems.vue
index ab9990a..63ae7e9 100644
--- a/erpnext/public/js/hub/pages/FeaturedItems.vue
+++ b/erpnext/public/js/hub/pages/FeaturedItems.vue
@@ -33,10 +33,8 @@
// Constants
page_title: __('Your Featured Items'),
- empty_state_message: __(`No featured items yet. Got to your
- <a href="#marketplace/published-items">
- Published Items</a>
- and feature upto 8 items that you want to highlight to your customers.`)
+ empty_state_message: __('No featured items yet. Got to your {0} and feature up to eight items that you want to highlight to your customers.',
+ [`<a href="#marketplace/published-items">${__("Published Items")}</a>`])
};
},
created() {
@@ -71,9 +69,9 @@
const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
- alert = frappe.show_alert(__(`<span>${item_name} removed.
- <a href="#" data-action="undo-remove"><b>Undo</b></a></span>`),
- grace_period/1000,
+ alert_message = __('{0} removed. {1}', [item_name,
+ `<a href="#" data-action="undo-remove"><b>${__('Undo')}</b></a>`]);
+ alert = frappe.show_alert(alert_message, grace_period / 1000,
{
'undo-remove': undo_remove.bind(this)
}
diff --git a/erpnext/public/js/hub/pages/Item.vue b/erpnext/public/js/hub/pages/Item.vue
index 51ade42..93002a7 100644
--- a/erpnext/public/js/hub/pages/Item.vue
+++ b/erpnext/public/js/hub/pages/Item.vue
@@ -113,12 +113,12 @@
let stats = __('No views yet');
if (this.item.view_count) {
- const views_message = __(`${this.item.view_count} Views`);
+ const views_message = __('{0} Views', [this.item.view_count]);
const rating_html = get_rating_html(this.item.average_rating);
const rating_count =
this.item.no_of_ratings > 0
- ? `${this.item.no_of_ratings} reviews`
+ ? __('{0} reviews', [this.item.no_of_ratings])
: __('No reviews yet');
stats = [views_message, rating_html, rating_count];
@@ -310,7 +310,7 @@
return this.get_item_details();
})
.then(() => {
- frappe.show_alert(__(`${this.item.item_name} Updated`));
+ frappe.show_alert(__('{0} Updated', [this.item.item_name]));
});
},
@@ -337,7 +337,7 @@
},
unpublish_item() {
- frappe.confirm(__(`Unpublish {0}?`, [this.item.item_name]), () => {
+ frappe.confirm(__('Unpublish {0}?', [this.item.item_name]), () => {
frappe
.call('erpnext.hub_node.api.unpublish_item', {
item_code: this.item.item_code,
diff --git a/erpnext/public/js/hub/pages/NotFound.vue b/erpnext/public/js/hub/pages/NotFound.vue
index 246d31b..8901b97 100644
--- a/erpnext/public/js/hub/pages/NotFound.vue
+++ b/erpnext/public/js/hub/pages/NotFound.vue
@@ -27,7 +27,7 @@
},
// Constants
- empty_state_message: __(`Sorry! I could not find what you were looking for.`)
+ empty_state_message: __('Sorry! We could not find what you were looking for.')
};
},
}
diff --git a/erpnext/public/js/hub/pages/Publish.vue b/erpnext/public/js/hub/pages/Publish.vue
index 735f2b9..96fa0aa 100644
--- a/erpnext/public/js/hub/pages/Publish.vue
+++ b/erpnext/public/js/hub/pages/Publish.vue
@@ -75,14 +75,11 @@
// TODO: multiline translations don't work
page_title: __('Publish Items'),
search_placeholder: __('Search Items ...'),
- empty_state_message: __(`No Items selected yet. Browse and click on items below to publish.`),
- valid_items_instruction: __(`Only items with an image and description can be published. Please update them if an item in your inventory does not appear.`),
+ empty_state_message: __('No Items selected yet. Browse and click on items below to publish.'),
+ valid_items_instruction: __('Only items with an image and description can be published. Please update them if an item in your inventory does not appear.'),
last_sync_message: (hub.settings.last_sync_datetime)
- ? __(`Last sync was
- <a href="#marketplace/profile">
- ${comment_when(hub.settings.last_sync_datetime)}</a>.
- <a href="#marketplace/published-items">
- See your Published Items</a>.`)
+ ? __('Last sync was {0}.', [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]) +
+ ` <a href="#marketplace/published-items">${__('See your Published Items.')}</a>`
: ''
};
},
@@ -147,11 +144,9 @@
},
add_last_sync_message() {
- this.last_sync_message = __(`Last sync was
- <a href="#marketplace/profile">
- ${comment_when(hub.settings.last_sync_datetime)}</a>.
- <a href="#marketplace/published-items">
- See your Published Items</a>.`);
+ this.last_sync_message = __('Last sync was {0}.',
+ [`<a href="#marketplace/profile">${comment_when(hub.settings.last_sync_datetime)}</a>`]
+ ) + `<a href="#marketplace/published-items">${__('See your Published Items')}</a>.`;
},
clear_last_sync_message() {
diff --git a/erpnext/public/js/hub/pages/SavedItems.vue b/erpnext/public/js/hub/pages/SavedItems.vue
index c29675a..7007ddc 100644
--- a/erpnext/public/js/hub/pages/SavedItems.vue
+++ b/erpnext/public/js/hub/pages/SavedItems.vue
@@ -29,7 +29,7 @@
// Constants
page_title: __('Saved Items'),
- empty_state_message: __(`You haven't saved any items yet.`)
+ empty_state_message: __('You have not saved any items yet.')
};
},
created() {
@@ -64,8 +64,13 @@
const item_name = this.items.filter(item => item.hub_item_name === hub_item_name);
- alert = frappe.show_alert(__(`<span>${item_name} removed.
- <a href="#" data-action="undo-remove"><b>Undo</b></a></span>`),
+ alert = frappe.show_alert(`
+ <span>
+ ${__('{0} removed.', [item_name], 'A specific Item has been removed.')}
+ <a href="#" data-action="undo-remove">
+ <b>${__('Undo', None, 'Undo removal of item.')}</b>
+ </a>
+ </span>`,
grace_period/1000,
{
'undo-remove': undo_remove.bind(this)
diff --git a/erpnext/public/js/hub/pages/Search.vue b/erpnext/public/js/hub/pages/Search.vue
index 1032842..c10841e 100644
--- a/erpnext/public/js/hub/pages/Search.vue
+++ b/erpnext/public/js/hub/pages/Search.vue
@@ -42,7 +42,10 @@
computed: {
page_title() {
return this.items.length
- ? __(`Results for "${this.search_value}" ${this.category !== 'All'? `in category ${this.category}` : ''}`)
+ ? __('Results for "{0}" {1}', [
+ this.search_value,
+ this.category !== 'All' ? __('in category {0}', [this.category]) : ''
+ ])
: __('No Items found.');
}
},
diff --git a/erpnext/public/js/hub/pages/Seller.vue b/erpnext/public/js/hub/pages/Seller.vue
index e339eaa..c0903c6 100644
--- a/erpnext/public/js/hub/pages/Seller.vue
+++ b/erpnext/public/js/hub/pages/Seller.vue
@@ -136,7 +136,7 @@
this.init = false;
this.profile = data.profile;
this.items = data.items;
- this.item_container_heading = data.is_featured_item? "Features Items":"Popular Items";
+ this.item_container_heading = data.is_featured_item ? __('Featured Items') : __('Popular Items');
this.hub_seller = this.items[0].hub_seller;
this.recent_seller_reviews = data.recent_seller_reviews;
this.seller_product_view_stats = data.seller_product_view_stats;
@@ -147,7 +147,7 @@
this.country = __(profile.country);
this.site_name = __(profile.site_name);
- this.joined_when = __(`Joined ${comment_when(profile.creation)}`);
+ this.joined_when = __('Joined {0}', [comment_when(profile.creation)]);
this.image = profile.logo;
this.sections = [
diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js
index 5d21190..092f839 100644
--- a/erpnext/public/js/setup_wizard.js
+++ b/erpnext/public/js/setup_wizard.js
@@ -161,7 +161,10 @@
if(r.message){
exist = r.message;
me.get_field("bank_account").set_value("");
- frappe.msgprint(__(`Account ${me.values.bank_account} already exists, enter a different name for your bank account`));
+ let message = __('Account {0} already exists. Please enter a different name for your bank account.',
+ [me.values.bank_account]
+ );
+ frappe.msgprint(message);
}
}
});
diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js
index 3b6a28f..3c15647 100644
--- a/erpnext/regional/india/taxes.js
+++ b/erpnext/regional/india/taxes.js
@@ -31,12 +31,12 @@
args: {
party_details: JSON.stringify(party_details),
doctype: frm.doc.doctype,
- company: frm.doc.company,
- return_taxes: 1
+ company: frm.doc.company
},
callback: function(r) {
if(r.message) {
frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
+ frm.set_value('place_of_supply', r.message.place_of_supply);
} else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) {
frm.set_value('taxes_and_charges', '');
frm.set_value('taxes', []);
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 5458001..62487ba 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -51,6 +51,13 @@
frappe.throw(_("Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.")
.format(doc.gst_state_number))
+def validate_tax_category(doc, method):
+ if doc.get('gst_state') and frappe.db.get_value('Tax category', {'gst_state': doc.gst_state, 'is_inter_state': doc.is_inter_state}):
+ if doc.is_inter_state:
+ frappe.throw(_("Inter State tax category for GST State {0} already exists").format(doc.gst_state))
+ else:
+ frappe.throw(_("Intra State tax category for GST State {0} already exists").format(doc.gst_state))
+
def update_gst_category(doc, method):
for link in doc.links:
if link.link_doctype in ['Customer', 'Supplier']:
@@ -85,8 +92,7 @@
total += digit
factor = 2 if factor == 1 else 1
if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]:
- frappe.throw(_("""Invalid {0}! The check digit validation has failed.
- Please ensure you've typed the {0} correctly.""".format(label)))
+ frappe.throw(_("""Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""").format(label))
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
if frappe.get_meta(item_doctype).has_field('gst_hsn_code'):
@@ -150,7 +156,7 @@
return cstr(address.gst_state_number) + "-" + cstr(address.gst_state)
@frappe.whitelist()
-def get_regional_address_details(party_details, doctype, company, return_taxes=None):
+def get_regional_address_details(party_details, doctype, company):
if isinstance(party_details, string_types):
party_details = json.loads(party_details)
party_details = frappe._dict(party_details)
@@ -160,7 +166,7 @@
if is_internal_transfer(party_details, doctype):
party_details.taxes_and_charges = ''
party_details.taxes = ''
- return
+ return party_details
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
master_doctype = "Sales Taxes and Charges Template"
@@ -168,26 +174,26 @@
get_tax_template_for_sez(party_details, master_doctype, company, 'Customer')
get_tax_template_based_on_category(master_doctype, company, party_details)
- if party_details.get('taxes_and_charges') and return_taxes:
+ if party_details.get('taxes_and_charges'):
return party_details
if not party_details.company_gstin:
- return
+ return party_details
elif doctype in ("Purchase Invoice", "Purchase Order", "Purchase Receipt"):
master_doctype = "Purchase Taxes and Charges Template"
get_tax_template_for_sez(party_details, master_doctype, company, 'Supplier')
get_tax_template_based_on_category(master_doctype, company, party_details)
- if party_details.get('taxes_and_charges') and return_taxes:
+ if party_details.get('taxes_and_charges'):
return party_details
if not party_details.supplier_gstin:
- return
+ return party_details
- if not party_details.place_of_supply: return
+ if not party_details.place_of_supply: return party_details
- if not party_details.company_gstin: return
+ if not party_details.company_gstin: return party_details
if ((doctype in ("Sales Invoice", "Delivery Note", "Sales Order") and party_details.company_gstin
and party_details.company_gstin[:2] != party_details.place_of_supply[:2]) or (doctype in ("Purchase Invoice",
@@ -197,12 +203,11 @@
default_tax = get_tax_template(master_doctype, company, 0, party_details.company_gstin[:2])
if not default_tax:
- return
+ return party_details
party_details["taxes_and_charges"] = default_tax
party_details.taxes = get_taxes_and_charges(master_doctype, default_tax)
- if return_taxes:
- return party_details
+ return party_details
def is_internal_transfer(party_details, doctype):
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
@@ -516,7 +521,7 @@
data.transType = 1
data.actualToStateCode = data.toStateCode
shipping_address = billing_address
-
+
if doc.gst_category == 'SEZ':
data.toStateCode = 99
@@ -755,4 +760,4 @@
}, account_currency, item=tax)
)
- return gl_entries
\ No newline at end of file
+ return gl_entries
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 3b62c38..be845d9 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -977,15 +977,20 @@
# For "Is Stock Item", following doctypes is important
# because reserved_qty, ordered_qty and requested_qty updated from these doctypes
if field == "is_stock_item":
- linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item"]
+ linked_doctypes += ["Sales Order Item", "Purchase Order Item", "Material Request Item", "Product Bundle"]
for doctype in linked_doctypes:
+ filters={"item_code": self.name, "docstatus": 1}
+
+ if doctype == "Product Bundle":
+ filters={"new_item_code": self.name}
+
if doctype in ("Purchase Invoice Item", "Sales Invoice Item",):
# If Invoice has Stock impact, only then consider it.
if self.stock_ledger_created():
return True
- elif frappe.db.get_value(doctype, filters={"item_code": self.name, "docstatus": 1}):
+ elif frappe.db.get_value(doctype, filters):
return True
def validate_auto_reorder_enabled_in_stock_settings(self):
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 9dee9a9..7e619bd 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -621,7 +621,8 @@
"doctype": "Purchase Invoice",
"field_map": {
"supplier_warehouse":"supplier_warehouse",
- "is_return": "is_return"
+ "is_return": "is_return",
+ "bill_date": "bill_date"
},
"validation": {
"docstatus": ["=", 1],
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js
index 22f29e0..376848a 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js
@@ -31,17 +31,27 @@
// item code based on GRN/DN
cur_frm.fields_dict['item_code'].get_query = function(doc, cdt, cdn) {
- const doctype = (doc.reference_type == "Stock Entry") ?
- "Stock Entry Detail" : doc.reference_type + " Item";
+ let doctype = doc.reference_type;
+
+ if (doc.reference_type !== "Job Card") {
+ doctype = (doc.reference_type == "Stock Entry") ?
+ "Stock Entry Detail" : doc.reference_type + " Item";
+ }
if (doc.reference_type && doc.reference_name) {
+ let filters = {
+ "from": doctype,
+ "inspection_type": doc.inspection_type
+ };
+
+ if (doc.reference_type == doctype)
+ filters["reference_name"] = doc.reference_name;
+ else
+ filters["parent"] = doc.reference_name;
+
return {
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
- filters: {
- "from": doctype,
- "parent": doc.reference_name,
- "inspection_type": doc.inspection_type
- }
+ filters: filters
};
}
},
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index dd95075..f6d7619 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -73,7 +73,7 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
- "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry",
+ "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry\nJob Card",
"reqd": 1
},
{
@@ -236,7 +236,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-21 13:03:11.938072",
+ "modified": "2020-11-19 17:06:05.409963",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection",
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index 399a63a..ae4eb9b 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -53,16 +53,28 @@
def update_qc_reference(self):
quality_inspection = self.name if self.docstatus == 1 else ""
- doctype = self.reference_type + ' Item'
- if self.reference_type == 'Stock Entry':
- doctype = 'Stock Entry Detail'
- if self.reference_type and self.reference_name:
- frappe.db.sql("""update `tab{child_doc}` t1, `tab{parent_doc}` t2
- set t1.quality_inspection = %s, t2.modified = %s
- where t1.parent = %s and t1.item_code = %s and t1.parent = t2.name"""
- .format(parent_doc=self.reference_type, child_doc=doctype),
- (quality_inspection, self.modified, self.reference_name, self.item_code))
+ if self.reference_type == 'Job Card':
+ if self.reference_name:
+ frappe.db.sql("""
+ UPDATE `tab{doctype}`
+ SET quality_inspection = %s, modified = %s
+ WHERE name = %s and production_item = %s
+ """.format(doctype=self.reference_type),
+ (quality_inspection, self.modified, self.reference_name, self.item_code))
+
+ else:
+ doctype = self.reference_type + ' Item'
+ if self.reference_type == 'Stock Entry':
+ doctype = 'Stock Entry Detail'
+
+ if self.reference_type and self.reference_name:
+ frappe.db.sql("""
+ UPDATE `tab{child_doc}` t1, `tab{parent_doc}` t2
+ SET t1.quality_inspection = %s, t2.modified = %s
+ WHERE t1.parent = %s and t1.item_code = %s and t1.parent = t2.name
+ """.format(parent_doc=self.reference_type, child_doc=doctype),
+ (quality_inspection, self.modified, self.reference_name, self.item_code))
def set_status_based_on_acceptance_formula(self):
for reading in self.readings:
@@ -95,27 +107,44 @@
mcond = get_match_cond(filters["from"])
cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')"
- if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']\
- and filters.get("inspection_type") != "In Process":
- cond = """and item_code in (select name from `tabItem` where
- inspection_required_before_purchase = 1)"""
- elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']\
- and filters.get("inspection_type") != "In Process":
- cond = """and item_code in (select name from `tabItem` where
- inspection_required_before_delivery = 1)"""
- elif filters.get('from') == 'Stock Entry Detail':
- cond = """and s_warehouse is null"""
+ if filters.get("parent"):
+ if filters.get('from') in ['Purchase Invoice Item', 'Purchase Receipt Item']\
+ and filters.get("inspection_type") != "In Process":
+ cond = """and item_code in (select name from `tabItem` where
+ inspection_required_before_purchase = 1)"""
+ elif filters.get('from') in ['Sales Invoice Item', 'Delivery Note Item']\
+ and filters.get("inspection_type") != "In Process":
+ cond = """and item_code in (select name from `tabItem` where
+ inspection_required_before_delivery = 1)"""
+ elif filters.get('from') == 'Stock Entry Detail':
+ cond = """and s_warehouse is null"""
- if filters.get('from') in ['Supplier Quotation Item']:
- qi_condition = ""
+ if filters.get('from') in ['Supplier Quotation Item']:
+ qi_condition = ""
- return frappe.db.sql(""" select item_code from `tab{doc}`
- where parent=%(parent)s and docstatus < 2 and item_code like %(txt)s
- {qi_condition} {cond} {mcond}
- order by item_code limit {start}, {page_len}""".format(doc=filters.get('from'),
- parent=filters.get('parent'), cond = cond, mcond = mcond, start = start,
- page_len = page_len, qi_condition = qi_condition),
- {'parent': filters.get('parent'), 'txt': "%%%s%%" % txt})
+ return frappe.db.sql("""
+ SELECT item_code
+ FROM `tab{doc}`
+ WHERE parent=%(parent)s and docstatus < 2 and item_code like %(txt)s
+ {qi_condition} {cond} {mcond}
+ ORDER BY item_code limit {start}, {page_len}
+ """.format(doc=filters.get('from'),
+ cond = cond, mcond = mcond, start = start,
+ page_len = page_len, qi_condition = qi_condition),
+ {'parent': filters.get('parent'), 'txt': "%%%s%%" % txt})
+
+ elif filters.get("reference_name"):
+ return frappe.db.sql("""
+ SELECT production_item
+ FROM `tab{doc}`
+ WHERE name = %(reference_name)s and docstatus < 2 and production_item like %(txt)s
+ {qi_condition} {cond} {mcond}
+ ORDER BY production_item
+ LIMIT {start}, {page_len}
+ """.format(doc=filters.get("from"),
+ cond = cond, mcond = mcond, start = start,
+ page_len = page_len, qi_condition = qi_condition),
+ {'reference_name': filters.get('reference_name'), 'txt': "%%%s%%" % txt})
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.json b/erpnext/stock/doctype/stock_settings/stock_settings.json
index 067659f..a166657 100644
--- a/erpnext/stock/doctype/stock_settings/stock_settings.json
+++ b/erpnext/stock/doctype/stock_settings/stock_settings.json
@@ -217,7 +217,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2020-10-13 10:33:29.147682",
+ "modified": "2020-11-23 15:26:54.225608",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Settings",
@@ -235,5 +235,6 @@
],
"quick_entry": 1,
"sort_field": "modified",
- "sort_order": "ASC"
+ "sort_order": "ASC",
+ "track_changes": 1
}
\ No newline at end of file