Merge pull request #26240 from rohitwaghchaure/production-plan-sub-assembly-enhancement
feat: provision to make subcontracted purchase order from the production plan
diff --git a/erpnext/hr/doctype/attendance/attendance.js b/erpnext/hr/doctype/attendance/attendance.js
index c3c3cb8..7964078 100644
--- a/erpnext/hr/doctype/attendance/attendance.js
+++ b/erpnext/hr/doctype/attendance/attendance.js
@@ -11,5 +11,5 @@
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
- }
+ }
}
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index f3b8a79..3412675 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -15,6 +15,7 @@
validate_status(self.status, ["Present", "Absent", "On Leave", "Half Day", "Work From Home"])
self.validate_attendance_date()
self.validate_duplicate_record()
+ self.validate_employee_status()
self.check_leave_record()
def validate_attendance_date(self):
@@ -38,6 +39,10 @@
frappe.throw(_("Attendance for employee {0} is already marked for the date {1}").format(
frappe.bold(self.employee), frappe.bold(self.attendance_date)))
+ def validate_employee_status(self):
+ if frappe.db.get_value("Employee", self.employee, "status") == "Inactive":
+ frappe.throw(_("Cannot mark attendance for an Inactive employee {0}").format(self.employee))
+
def check_leave_record(self):
leave_record = frappe.db.sql("""
select leave_type, half_day, half_day_date
diff --git a/erpnext/hr/doctype/attendance/attendance_list.js b/erpnext/hr/doctype/attendance/attendance_list.js
index 0c7eafe..9a3bac0 100644
--- a/erpnext/hr/doctype/attendance/attendance_list.js
+++ b/erpnext/hr/doctype/attendance/attendance_list.js
@@ -21,6 +21,9 @@
label: __('For Employee'),
fieldtype: 'Link',
options: 'Employee',
+ get_query: () => {
+ return {query: "erpnext.controllers.queries.employee_query"}
+ },
reqd: 1,
onchange: function() {
dialog.set_df_property("unmarked_days", "hidden", 1);
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
index 18bd4ae..68bac8e 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.json
@@ -35,7 +35,9 @@
"no_copy": 1,
"options": "Loan Security Pledge",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fetch_from": "loan_application.applicant",
@@ -45,47 +47,63 @@
"in_standard_filter": 1,
"label": "Applicant",
"options": "applicant_type",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "loan_security_details_section",
"fieldtype": "Section Break",
- "label": "Loan Security Details"
+ "label": "Loan Security Details",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_3",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "loan",
"fieldtype": "Link",
"label": "Loan",
- "options": "Loan"
+ "options": "Loan",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "loan_application",
"fieldtype": "Link",
"label": "Loan Application",
- "options": "Loan Application"
+ "options": "Loan Application",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "total_security_value",
"fieldtype": "Currency",
"label": "Total Security Value",
"options": "Company:company:default_currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "maximum_loan_value",
"fieldtype": "Currency",
"label": "Maximum Loan Value",
"options": "Company:company:default_currency",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "loan_details_section",
"fieldtype": "Section Break",
- "label": "Loan Details"
+ "label": "Loan Details",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"default": "Requested",
@@ -94,37 +112,49 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Status",
- "options": "Requested\nUnpledged\nPledged\nPartially Pledged",
- "read_only": 1
+ "options": "Requested\nUnpledged\nPledged\nPartially Pledged\nCancelled",
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "pledge_time",
"fieldtype": "Datetime",
"label": "Pledge Time",
- "read_only": 1
+ "read_only": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "securities",
"fieldtype": "Table",
"label": "Securities",
"options": "Pledge",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_11",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "section_break_10",
"fieldtype": "Section Break",
- "label": "Totals"
+ "label": "Totals",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fetch_from": "loan.applicant_type",
@@ -132,35 +162,45 @@
"fieldtype": "Select",
"label": "Applicant Type",
"options": "Employee\nMember\nCustomer",
- "reqd": 1
+ "reqd": 1,
+ "show_days": 1,
+ "show_seconds": 1
},
{
"collapsible": 1,
"fieldname": "more_information_section",
"fieldtype": "Section Break",
- "label": "More Information"
+ "label": "More Information",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "reference_no",
"fieldtype": "Data",
- "label": "Reference No"
+ "label": "Reference No",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"fieldname": "column_break_18",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "show_days": 1,
+ "show_seconds": 1
},
{
"allow_on_submit": 1,
"fieldname": "description",
"fieldtype": "Text",
- "label": "Description"
+ "label": "Description",
+ "show_days": 1,
+ "show_seconds": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-04-19 18:23:16.953305",
+ "modified": "2021-06-29 17:15:16.082256",
"modified_by": "Administrator",
"module": "Loan Management",
"name": "Loan Security Pledge",
diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
index cbc8376..c390b6c 100644
--- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
+++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.py
@@ -23,6 +23,12 @@
update_shortfall_status(self.loan, self.total_security_value)
update_loan(self.loan, self.maximum_loan_value)
+ def on_cancel(self):
+ if self.loan:
+ self.db_set("status", "Cancelled")
+ self.db_set("pledge_time", None)
+ update_loan(self.loan, self.maximum_loan_value, cancel=1)
+
def validate_duplicate_securities(self):
security_list = []
for security in self.securities:
@@ -36,7 +42,7 @@
existing_pledge = ''
if self.loan:
- existing_pledge = frappe.db.get_value('Loan Security Pledge', {'loan': self.loan}, ['name'])
+ existing_pledge = frappe.db.get_value('Loan Security Pledge', {'loan': self.loan, 'docstatus': 1}, ['name'])
if existing_pledge:
loan_security_type = frappe.db.get_value('Pledge', {'parent': existing_pledge}, ['loan_security_type'])
@@ -77,8 +83,12 @@
self.total_security_value = total_security_value
self.maximum_loan_value = maximum_loan_value
-def update_loan(loan, maximum_value_against_pledge):
+def update_loan(loan, maximum_value_against_pledge, cancel=0):
maximum_loan_value = frappe.db.get_value('Loan', {'name': loan}, ['maximum_loan_amount'])
- frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
- WHERE name=%s""", (maximum_loan_value + maximum_value_against_pledge, loan))
+ if cancel:
+ frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s
+ WHERE name=%s""", (maximum_loan_value - maximum_value_against_pledge, loan))
+ else:
+ frappe.db.sql(""" UPDATE `tabLoan` SET maximum_loan_amount=%s, is_secured_loan=1
+ WHERE name=%s""", (maximum_loan_value + maximum_value_against_pledge, loan))
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
index 4c31bd0..f19a1b0 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js
@@ -13,7 +13,7 @@
refresh: function(frm) {
erpnext.hide_company();
- if (frm.doc.customer && frm.doc.docstatus === 1) {
+ if (frm.doc.customer && frm.doc.docstatus === 1 && frm.doc.to_date > frappe.datetime.get_today()) {
frm.add_custom_button(__("Sales Order"), function() {
frappe.model.open_mapped_doc({
method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order",
diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
index 0330e5c..a63fc4d 100644
--- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
+++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "naming_series:",
"creation": "2018-05-24 07:18:08.256060",
"doctype": "DocType",
@@ -79,6 +80,7 @@
"reqd": 1
},
{
+ "allow_on_submit": 1,
"fieldname": "to_date",
"fieldtype": "Date",
"label": "To Date",
@@ -129,8 +131,10 @@
"label": "Terms and Conditions Details"
}
],
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
- "modified": "2019-11-18 19:37:37.151686",
+ "links": [],
+ "modified": "2021-06-29 00:30:30.621636",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Blanket Order",
diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
index e71d81f..5c7c0a3 100644
--- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py
@@ -459,6 +459,7 @@
where
t1.name = t2.employee
and t2.docstatus = 1
+ and t1.status != 'Inactive'
%s order by t2.from_date desc
""" % cond, {"sal_struct": tuple(sal_struct), "from_date": end_date, "payroll_payable_account": payroll_payable_account}, as_dict=True)
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index e646600..dd94e7c 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -404,17 +404,18 @@
key = (d.item_code, d.warehouse)
if key not in merge_similar_entries:
+ d.total_amount = (d.actual_qty * d.valuation_rate)
merge_similar_entries[key] = d
elif d.serial_no:
data = merge_similar_entries[key]
data.actual_qty += d.actual_qty
data.qty_after_transaction += d.qty_after_transaction
- data.valuation_rate = (data.valuation_rate + d.valuation_rate) / data.actual_qty
+ data.total_amount += (d.actual_qty * d.valuation_rate)
+ data.valuation_rate = (data.total_amount) / data.actual_qty
data.serial_no += '\n' + d.serial_no
- if data.incoming_rate:
- data.incoming_rate = (data.incoming_rate + d.incoming_rate) / data.actual_qty
+ data.incoming_rate = (data.total_amount) / data.actual_qty
for key, value in merge_similar_entries.items():
new_sl_entries.append(value)
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 36380b8..ce4cbd2 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -6,7 +6,7 @@
from __future__ import unicode_literals
import frappe, unittest
-from frappe.utils import flt, nowdate, nowtime
+from frappe.utils import flt, nowdate, nowtime, random_string
from erpnext.accounts.utils import get_stock_and_account_balance
from erpnext.stock.stock_ledger import get_previous_sle, update_entries_after
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import EmptyStockReconciliationItemsError, get_items
@@ -150,6 +150,42 @@
stock_doc = frappe.get_doc("Stock Reconciliation", d)
stock_doc.cancel()
+
+ def test_stock_reco_for_merge_serialized_item(self):
+ to_delete_records = []
+
+ # Add new serial nos
+ serial_item_code = "Stock-Reco-Serial-Item-2"
+ serial_warehouse = "_Test Warehouse for Stock Reco1 - _TC"
+
+ sr = create_stock_reconciliation(item_code=serial_item_code, serial_no=random_string(6),
+ warehouse = serial_warehouse, qty=1, rate=100, do_not_submit=True, purpose='Opening Stock')
+
+ for i in range(3):
+ sr.append('items', {
+ 'item_code': serial_item_code,
+ 'warehouse': serial_warehouse,
+ 'qty': 1,
+ 'valuation_rate': 100,
+ 'serial_no': random_string(6)
+ })
+
+ sr.save()
+ sr.submit()
+
+ sle_entries = frappe.get_all('Stock Ledger Entry', filters= {'voucher_no': sr.name},
+ fields = ['name', 'incoming_rate'])
+
+ self.assertEqual(len(sle_entries), 1)
+ self.assertEqual(sle_entries[0].incoming_rate, 100)
+
+ to_delete_records.append(sr.name)
+ to_delete_records.reverse()
+
+ for d in to_delete_records:
+ stock_doc = frappe.get_doc("Stock Reconciliation", d)
+ stock_doc.cancel()
+
def test_stock_reco_for_batch_item(self):
to_delete_records = []
to_delete_serial_nos = []
@@ -231,6 +267,12 @@
serial_item_doc.serial_no_series = "SRSI.####"
serial_item_doc.save(ignore_permissions=True)
+ serial_item_doc = create_item("Stock-Reco-Serial-Item-2", is_stock_item=1)
+ if not serial_item_doc.has_serial_no:
+ serial_item_doc.has_serial_no = 1
+ serial_item_doc.serial_no_series = "SRSII.####"
+ serial_item_doc.save(ignore_permissions=True)
+
batch_item_doc = create_item("Stock-Reco-batch-Item-1", is_stock_item=1)
if not batch_item_doc.has_batch_no:
batch_item_doc.has_batch_no = 1