Merge branch 'develop' into dialog-cleanup
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index 79fcb95..d422876 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -55,6 +55,7 @@
"fieldtype": "Date",
"in_list_view": 1,
"label": "Depreciation Posting Date",
+ "mandatory_depends_on": "eval:parent.doctype == 'Asset'",
"reqd": 1
},
{
@@ -86,7 +87,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2020-09-16 12:11:30.631788",
+ "modified": "2020-10-30 15:22:29.119868",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 0181ae7..d568ef1 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -876,7 +876,7 @@
},
{
"item_code":item_code,"rm_item_code":"Sub Contracted Raw Material 4","item_name":"_Test Item",
- "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos", "name": po.supplied_items[1].name
+ "qty":250,"warehouse":"_Test Warehouse - _TC", "stock_uom":"Nos"
},
]
@@ -885,6 +885,10 @@
se = frappe.get_doc(make_subcontract_transfer_entry(po.name, rm_item_string))
se.submit()
+ # Test po_detail field has value or not
+ for item_row in se.items:
+ self.assertEqual(item_row.po_detail, po.supplied_items[item_row.idx - 1].name)
+
po_doc = frappe.get_doc("Purchase Order", po.name)
for row in po_doc.supplied_items:
# Valid that whether transferred quantity is matching with supplied qty or not in the purchase order
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index f376836..9ee83e3 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -301,7 +301,7 @@
# backflushed_batch_qty_map = get_backflushed_batch_qty_map(item.purchase_order, item.item_code)
for raw_material in transferred_raw_materials + non_stock_items:
- rm_item_key = (raw_material.rm_item_code, item.purchase_order)
+ rm_item_key = (raw_material.rm_item_code, item.item_code, item.purchase_order)
raw_material_data = backflushed_raw_materials_map.get(rm_item_key, {})
consumed_qty = raw_material_data.get('qty', 0)
@@ -910,7 +910,7 @@
purchase_receipt_supplied_items = get_supplied_items(args[1], args[2], references)
for data in purchase_receipt_supplied_items:
- pr_key = (data.rm_item_code, args[0])
+ pr_key = (data.rm_item_code, data.main_item_code, args[0])
if pr_key not in backflushed_raw_materials_map:
backflushed_raw_materials_map.setdefault(pr_key, frappe._dict({
"qty": 0.0,
@@ -936,7 +936,7 @@
def get_supplied_items(item_code, purchase_receipt, references):
return frappe.get_all("Purchase Receipt Item Supplied",
- fields=["rm_item_code", "consumed_qty", "serial_no", "batch_no"],
+ fields=["rm_item_code", "main_item_code", "consumed_qty", "serial_no", "batch_no"],
filters={"main_item_code": item_code, "parent": purchase_receipt, "reference_name": ("in", references)})
def get_asset_item_details(asset_items):
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
index b953b8a..f523cf2 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.js
@@ -21,6 +21,19 @@
}
};
});
+
+ 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) {
diff --git a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
index 5d80251..dd4c423 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.json
@@ -67,6 +67,7 @@
},
{
"collapsible": 1,
+ "collapsible_depends_on": "eval: doc.__islocal",
"fieldname": "filters_section",
"fieldtype": "Section Break",
"label": "Filters"
@@ -93,6 +94,7 @@
"options": "Patient"
},
{
+ "depends_on": "eval:!doc.patient",
"fieldname": "service_unit",
"fieldtype": "Link",
"label": "Healthcare Service Unit",
@@ -178,7 +180,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-09-30 23:40:45.528715",
+ "modified": "2020-11-03 13:22:37.820707",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Inpatient Medication Entry",
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 2385893..23e7519 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
+++ b/erpnext/healthcare/doctype/inpatient_medication_entry/inpatient_medication_entry.py
@@ -199,6 +199,7 @@
def get_pending_medication_orders(entry):
filters, values = get_filters(entry)
+ to_remove = []
data = frappe.db.sql("""
SELECT
@@ -225,7 +226,10 @@
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
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
diff --git a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js b/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js
index c51f3cf..690e2e7 100644
--- a/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js
+++ b/erpnext/healthcare/doctype/inpatient_medication_order/inpatient_medication_order.js
@@ -12,7 +12,8 @@
frm.set_query('patient', () => {
return {
filters: {
- 'inpatient_record': ['!=', '']
+ 'inpatient_record': ['!=', ''],
+ 'inpatient_status': 'Admitted'
}
};
});
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 1a64bc5..b723387 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -56,23 +56,35 @@
refresh: function(frm) {
if (frm.doc.docstatus === 1) {
frm.trigger("show_progress");
+
+ if (frm.doc.status !== "Completed") {
+ if (frm.doc.po_items && frm.doc.status !== "Closed") {
+ frm.add_custom_button(__("Work Order"), ()=> {
+ frm.trigger("make_work_order");
+ }, __('Create'));
+ }
+
+ if (frm.doc.mr_items && !in_list(['Material Requested', 'Closed'], frm.doc.status)) {
+ frm.add_custom_button(__("Material Request"), ()=> {
+ frm.trigger("make_material_request");
+ }, __('Create'));
+ }
+
+ if (frm.doc.status === "Closed") {
+ frm.add_custom_button(__("Re-open"), function() {
+ frm.events.close_open_production_plan(frm, false);
+ }, __("Status"));
+ } else {
+ frm.add_custom_button(__("Close"), function() {
+ frm.events.close_open_production_plan(frm, true);
+ }, __("Status"));
+ }
+ }
}
- if (frm.doc.docstatus === 1 && frm.doc.po_items
- && frm.doc.status != 'Completed') {
- frm.add_custom_button(__("Work Order"), ()=> {
- frm.trigger("make_work_order");
- }, __('Create'));
+ if (frm.doc.status !== "Closed") {
+ frm.page.set_inner_btn_group_as_primary(__('Create'));
}
-
- if (frm.doc.docstatus === 1 && frm.doc.mr_items
- && !in_list(['Material Requested', 'Completed'], frm.doc.status)) {
- frm.add_custom_button(__("Material Request"), ()=> {
- frm.trigger("make_material_request");
- }, __('Create'));
- }
-
- frm.page.set_inner_btn_group_as_primary(__('Create'));
frm.trigger("material_requirement");
const projected_qty_formula = ` <table class="table table-bordered" style="background-color: #f9f9f9;">
@@ -121,6 +133,18 @@
set_field_options("projected_qty_formula", projected_qty_formula);
},
+ close_open_production_plan: (frm, close=false) => {
+ frappe.call({
+ method: "set_status",
+ freeze: true,
+ doc: frm.doc,
+ args: {close : close},
+ callback: function() {
+ frm.reload_doc();
+ }
+ });
+ },
+
make_work_order: function(frm) {
frappe.call({
method: "make_work_order",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 90e8b22..850d5ae 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -275,7 +275,7 @@
"fieldtype": "Select",
"label": "Status",
"no_copy": 1,
- "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nStopped\nCancelled\nMaterial Requested",
+ "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested",
"print_hide": 1,
"read_only": 1
},
@@ -304,9 +304,10 @@
}
],
"icon": "fa fa-calendar",
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-02-03 00:25:25.934202",
+ "modified": "2020-10-26 13:00:54.335319",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index feac9c8..a314a15 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -219,13 +219,17 @@
filters = {'docstatus': 0, 'production_plan': ("=", self.name)}):
frappe.delete_doc('Work Order', d.name)
- def set_status(self):
+ def set_status(self, close=None):
self.status = {
0: 'Draft',
1: 'Submitted',
2: 'Cancelled'
}.get(self.docstatus)
+ if close:
+ self.db_set('status', 'Closed')
+ return
+
if self.total_produced_qty > 0:
self.status = "In Process"
if self.total_produced_qty == self.total_planned_qty:
@@ -235,6 +239,9 @@
self.update_ordered_status()
self.update_requested_status()
+ if close is not None:
+ self.db_set('status', self.status)
+
def update_ordered_status(self):
update_status = False
for d in self.po_items:
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
index d377ef0..165b66f 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js
@@ -1,6 +1,6 @@
frappe.listview_settings['Production Plan'] = {
add_fields: ["status"],
- filters: [["status", "!=", "Stopped"]],
+ filters: [["status", "!=", "Closed"]],
get_indicator: function(doc) {
if(doc.status==="Submitted") {
return [__("Not Started"), "orange", "status,=,Submitted"];
@@ -10,7 +10,8 @@
"In Process": "orange",
"Completed": "green",
"Material Requested": "darkgrey",
- "Cancelled": "darkgrey"
+ "Cancelled": "darkgrey",
+ "Closed": "grey"
}[doc.status], "status,=," + doc.status];
}
}
diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
index 53e33c0..e72f489 100644
--- a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
+++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.json
@@ -11,30 +11,20 @@
{
"fieldname": "warehouse",
"fieldtype": "Link",
+ "in_list_view": 1,
"label": "Warehouse",
"options": "Warehouse"
}
],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
"links": [],
- "modified": "2020-02-02 10:37:16.650836",
+ "modified": "2020-10-26 12:55:00.778201",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Material Request Warehouse",
"owner": "Administrator",
- "permissions": [
- {
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- }
- ],
+ "permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 8b34eaa..5ef5371 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -732,3 +732,4 @@
erpnext.patches.v13_0.print_uom_after_quantity_patch
erpnext.patches.v13_0.set_payment_channel_in_payment_gateway_account
erpnext.patches.v13_0.create_healthcare_custom_fields_in_stock_entry_detail
+erpnext.patches.v13_0.update_reason_for_resignation_in_employee
diff --git a/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
new file mode 100644
index 0000000..792118f
--- /dev/null
+++ b/erpnext/patches/v13_0/update_reason_for_resignation_in_employee.py
@@ -0,0 +1,15 @@
+# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
+# MIT License. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("hr", "doctype", "employee")
+
+ if frappe.db.has_column("Employee", "reason_for_resignation"):
+ frappe.db.sql(""" UPDATE `tabEmployee`
+ SET reason_for_leaving = reason_for_resignation
+ WHERE status = 'Left' and reason_for_leaving is null and reason_for_resignation is not null
+ """)
+
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 69e47a4..dd87f0f 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -139,7 +139,7 @@
if not frappe.get_meta('Address').has_field('gst_state'): return
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"):
address_name = party_details.shipping_address or party_details.supplier_address
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py
index 6b9939e..a690050 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.py
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.py
@@ -14,11 +14,9 @@
def get_items(start, page_length, price_list, item_group, pos_profile, search_value=""):
data = dict()
result = []
- warehouse, hide_unavailable_items = "", False
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, hide_unavailable_items = frappe.db.get_value('POS Profile', pos_profile, ['warehouse', 'hide_unavailable_items'])
if not frappe.db.exists('Item Group', item_group):
item_group = get_root_of('Item Group')
@@ -97,7 +95,7 @@
for item in items_data:
item_code = item.item_code
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]
else:
item_stock_qty = get_stock_availability(item_code, warehouse)
@@ -231,13 +229,31 @@
frappe.db.set_value('Customer', customer, 'loyalty_program', value)
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:
- 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()
\ No newline at end of file
+ if not contact:
+ new_contact = frappe.new_doc('Contact')
+ new_contact.is_primary_contact = 1
+ new_contact.first_name = customer
+ new_contact.set('links', [{'link_doctype': 'Customer', 'link_name': customer}])
+ new_contact.save()
+ contact = new_contact.name
+ frappe.db.set_value('Customer', customer, 'customer_primary_contact', contact)
+
+ 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()
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index 3d00546..f7d1fa4 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -637,7 +637,7 @@
if (!(available_qty > 0)) {
frappe.model.clear_doc(item_row.doctype, item_row.name);
frappe.throw({
- title: _("Not Available"),
+ title: __("Not Available"),
message: __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse])
})
} else if (available_qty < qty_needed) {
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index 4139e29..49d4281 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -81,7 +81,7 @@
function get_item_image_html() {
if (item_image) {
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>`
} else {
return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index e89cf01..e4d8965 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -409,7 +409,7 @@
${
shortcuts.map(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>`
}).join('')
}
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 4583c51..7685267 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -615,6 +615,15 @@
if not row.subcontracted_item:
frappe.throw(_("Row {0}: Subcontracted Item is mandatory for the raw material {1}")
.format(row.idx, frappe.bold(row.item_code)))
+ elif not row.po_detail:
+ filters = {
+ "parent": self.purchase_order, "docstatus": 1,
+ "rm_item_code": row.item_code, "main_item_code": row.subcontracted_item
+ }
+
+ po_detail = frappe.db.get_value("Purchase Order Item Supplied", filters, "name")
+ if po_detail:
+ row.db_set("po_detail", po_detail)
def validate_bom(self):
for d in self.get('items'):