diff --git a/erpnext/__init__.py b/erpnext/__init__.py
index ea60039da09..4cd5982e877 100644
--- a/erpnext/__init__.py
+++ b/erpnext/__init__.py
@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
-__version__ = '12.22.0'
+__version__ = '12.23.0'
def get_default_company(user=None):
'''Get default company for user'''
diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js
index 6b221433aa3..9fa2083a355 100644
--- a/erpnext/accounts/doctype/bank/bank.js
+++ b/erpnext/accounts/doctype/bank/bank.js
@@ -109,5 +109,4 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
plaid_success(token, response) {
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
}
-};
-
+};
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
index 43ebcb0cac9..729d4ae2368 100644
--- a/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
+++ b/erpnext/accounts/doctype/cash_flow_mapper/default_cash_flow_mapper.py
@@ -7,19 +7,19 @@ DEFAULT_MAPPERS = [
'section_header': 'Cash flows from operating activities',
'section_leader': 'Adjustments for',
'section_name': 'Operating Activities',
- 'position': 0,
+ 'position': 1,
'section_subtotal': 'Cash generated from operations',
},
{
'doctype': 'Cash Flow Mapper',
- 'position': 1,
+ 'position': 2,
'section_footer': 'Net cash used in investing activities',
'section_header': 'Cash flows from investing activities',
'section_name': 'Investing Activities'
},
{
'doctype': 'Cash Flow Mapper',
- 'position': 2,
+ 'position': 3,
'section_footer': 'Net cash used in financing activites',
'section_header': 'Cash flows from financing activities',
'section_name': 'Financing Activities',
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index b4599ba0f46..7d4679690ea 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -97,8 +97,7 @@ class GLEntry(Document):
def check_pl_account(self):
if self.is_opening=='Yes' and \
- frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss" and \
- self.voucher_type not in ['Purchase Invoice', 'Sales Invoice']:
+ frappe.db.get_value("Account", self.account, "report_type")=="Profit and Loss":
frappe.throw(_("{0} {1}: 'Profit and Loss' type account {2} not allowed in Opening Entry")
.format(self.voucher_type, self.voucher_no, self.account))
diff --git a/erpnext/accounts/doctype/subscription/subscription.json b/erpnext/accounts/doctype/subscription/subscription.json
index fb5bb4839c4..bbe8cca9152 100644
--- a/erpnext/accounts/doctype/subscription/subscription.json
+++ b/erpnext/accounts/doctype/subscription/subscription.json
@@ -30,6 +30,7 @@
"additional_discount_percentage",
"additional_discount_amount",
"sb_3",
+ "submit_invoice",
"invoices",
"accounting_dimensions_section",
"dimension_col_break"
@@ -202,9 +203,15 @@
"fieldname": "generate_new_invoices_past_due_date",
"fieldtype": "Check",
"label": "Generate New Invoices Past Due Date"
+ },
+ {
+ "default": "1",
+ "fieldname": "submit_invoice",
+ "fieldtype": "Check",
+ "label": "Submit Invoice Automatically"
}
],
- "modified": "2020-11-29 22:46:14.879289",
+ "modified": "2021-05-03 13:35:21.422940",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription",
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 07fc68334a8..1abb93464b0 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -289,7 +289,9 @@ class Subscription(Document):
invoice.flags.ignore_mandatory = True
invoice.save()
- invoice.submit()
+
+ if self.submit_invoice:
+ invoice.submit()
return invoice
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 84480468dad..2c48b7995bf 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -355,7 +355,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
- per_ordered: ["<", 99.99],
+ per_ordered: ["<", 100],
}
})
}, __("Get items from"));
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 4a937f7f0d3..d284e2ef318 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -271,7 +271,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
- per_ordered: ["<", 99.99]
+ per_ordered: ["<", 100]
}
})
}, __("Get items from"));
@@ -316,7 +316,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
- per_ordered: ["<", 99.99]
+ per_ordered: ["<", 100]
}
});
$(btn).done_working();
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index bef2965bf02..51af59f5eeb 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -279,19 +279,21 @@ def add_items(sq_doc, supplier, items):
create_rfq_items(sq_doc, supplier, data)
def create_rfq_items(sq_doc, supplier, data):
- sq_doc.append('items', {
- "item_code": data.item_code,
- "item_name": data.item_name,
- "description": data.description,
- "qty": data.qty,
- "rate": data.rate,
- "conversion_factor": data.conversion_factor if data.conversion_factor else None,
- "supplier_part_no": frappe.db.get_value("Item Supplier", {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no"),
- "warehouse": data.warehouse or '',
+ args = {}
+
+ for field in ['item_code', 'item_name', 'description', 'qty', 'rate', 'conversion_factor',
+ 'warehouse', 'material_request', 'material_request_item', 'stock_qty']:
+ args[field] = data.get(field)
+
+ args.update({
"request_for_quotation_item": data.name,
- "request_for_quotation": data.parent
+ "request_for_quotation": data.parent,
+ "supplier_part_no": frappe.db.get_value("Item Supplier",
+ {'parent': data.item_code, 'supplier': supplier}, "supplier_part_no")
})
+ sq_doc.append('items', args)
+
@frappe.whitelist()
def get_pdf(doctype, name, supplier_idx):
doc = get_rfq_doc(doctype, name, supplier_idx)
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index 16061c61ba0..2fae2572a9b 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -46,7 +46,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
- per_ordered: ["<", 99.99]
+ per_ordered: ["<", 100]
}
})
}, __("Get items from"));
diff --git a/erpnext/change_log/v12/v12_23_0.md b/erpnext/change_log/v12/v12_23_0.md
new file mode 100644
index 00000000000..3b4ca5ce568
--- /dev/null
+++ b/erpnext/change_log/v12/v12_23_0.md
@@ -0,0 +1,20 @@
+## Version 12.23.0 Release Notes
+
+### Fixes & Enhancements
+- Added Permissions for employee to book an appointment ([#26246](https://github.com/frappe/erpnext/pull/26246))
+- New check field in subscriptions for (not) submitting invoices (BP #25394) ([#25560](https://github.com/frappe/erpnext/pull/25560))
+- fix(e-invoicing): allow export invoice even if no taxes applied (#26363) ([#26406](https://github.com/frappe/erpnext/pull/26406))
+- Omit item discount amount for e-invoicing (#26353) ([#26408](https://github.com/frappe/erpnext/pull/26408))
+- fix(plaid): cannot reset plaid link for a bank account ([#26282](https://github.com/frappe/erpnext/pull/26282))
+- Job applicant link issue ([#25935](https://github.com/frappe/erpnext/pull/25935))
+- LMS progress issue ([#26254](https://github.com/frappe/erpnext/pull/26254))
+- Half day to be accounted in its leave type ([#26267](https://github.com/frappe/erpnext/pull/26267))
+- Material request status issue ([#26089](https://github.com/frappe/erpnext/pull/26089))
+- fix(e-invoicing): service item check ([#26141](https://github.com/frappe/erpnext/pull/26141))
+- Invoices can alter profit and loss of a closed year ([#26161](https://github.com/frappe/erpnext/pull/26161))
+- Material request and supplier quotation not linked if supplier quotation created from supplier portal ([#26117](https://github.com/frappe/erpnext/pull/26117))
+- Update positions in default cashflow mappers ([#26091](https://github.com/frappe/erpnext/pull/26091))
+- Staffing plan vacancies data type issue ([#25940](https://github.com/frappe/erpnext/pull/25940))
+- Added company filter while fetching loans ([#26296](https://github.com/frappe/erpnext/pull/26296))
+- Serial no issue in subcontract purchase receipt ([#26423](https://github.com/frappe/erpnext/pull/26423))
+- Fixed rounding off ordered percent to 100 in condition ([#26153](https://github.com/frappe/erpnext/pull/26153))
\ No newline at end of file
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index 9d777ef31ea..e5012f972b2 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -981,8 +981,16 @@ def get_non_stock_items(purchase_order, fg_item_code):
def set_serial_nos(raw_material, consumed_serial_nos, qty):
- serial_nos = set(get_serial_nos(raw_material.serial_nos)) - \
- set(get_serial_nos(consumed_serial_nos))
+ consumed_serial_nos_list = []
+
+ if isinstance(consumed_serial_nos, list):
+ for row in consumed_serial_nos:
+ consumed_serial_nos_list.extend(get_serial_nos(row))
+ else:
+ consumed_serial_nos_list = get_serial_nos(row)
+
+ serial_nos = set(get_serial_nos(raw_material.serial_nos)) - set(consumed_serial_nos_list)
+
if serial_nos and qty <= len(serial_nos):
raw_material.serial_no = '\n'.join(list(serial_nos)[0:frappe.utils.cint(qty)])
diff --git a/erpnext/crm/doctype/appointment/appointment.json b/erpnext/crm/doctype/appointment/appointment.json
index 32df8ec4295..7dff80855bc 100644
--- a/erpnext/crm/doctype/appointment/appointment.json
+++ b/erpnext/crm/doctype/appointment/appointment.json
@@ -93,7 +93,7 @@
"fieldtype": "Column Break"
}
],
- "modified": "2019-10-14 15:23:54.630731",
+ "modified": "2021-06-28 16:27:53.235714",
"modified_by": "Administrator",
"module": "CRM",
"name": "Appointment",
@@ -144,6 +144,18 @@
"role": "Sales User",
"share": 1,
"write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Employee",
+ "share": 1,
+ "write": 1
}
],
"quick_entry": 1,
diff --git a/erpnext/education/utils.py b/erpnext/education/utils.py
index e0b278c2b1d..9f833dba328 100644
--- a/erpnext/education/utils.py
+++ b/erpnext/education/utils.py
@@ -345,11 +345,11 @@ def get_or_create_course_enrollment(course, program):
student = get_current_student()
course_enrollment = get_enrollment("course", course, student.name)
if not course_enrollment:
- program_enrollment = get_enrollment('program', program, student.name)
+ program_enrollment = get_enrollment('program', program.name, student.name)
if not program_enrollment:
frappe.throw(_("You are not enrolled in program {0}".format(program)))
return
- return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program, student.name))
+ return student.enroll_in_course(course_name=course, program_enrollment=get_enrollment('program', program.name, student.name))
else:
return frappe.get_doc('Course Enrollment', course_enrollment)
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
index 5f990cdd034..42d4b9b2b43 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_connector.py
@@ -99,5 +99,7 @@ class PlaidConnector():
response = self.client.Transactions.get(self.access_token, start_date=start_date, end_date=end_date, offset=len(transactions))
transactions.extend(response["transactions"])
return transactions
+ except ItemError as e:
+ raise e
except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
index 72705158251..6d59895258d 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.js
@@ -15,6 +15,10 @@ frappe.ui.form.on('Plaid Settings', {
frm.add_custom_button(__('Link a new bank account'), () => {
new erpnext.integrations.plaidLink(frm);
});
+
+ frm.add_custom_button(__('Reset Plaid Link'), () => {
+ new erpnext.integrations.plaidLink(frm);
+ });
}
}
});
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index 05b7d11b794..75b2bd6b810 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -12,6 +12,7 @@ from frappe.desk.doctype.tag.tag import add_tag
from frappe.model.document import Document
from frappe.utils import add_months, formatdate, getdate, today
+from plaid.errors import ItemError
class PlaidSettings(Document):
@staticmethod
@@ -50,7 +51,7 @@ def add_institution(token, response):
})
bank.insert()
except Exception:
- frappe.throw(frappe.get_traceback())
+ frappe.log_error(frappe.get_traceback(), title=_('Plaid Link Error'))
else:
bank = frappe.get_doc("Bank", response["institution"]["name"])
bank.plaid_access_token = access_token
@@ -82,7 +83,12 @@ def add_bank_accounts(response, bank, company):
if not acc_subtype:
add_account_subtype(account["subtype"])
- if not frappe.db.exists("Bank Account", dict(integration_id=account["id"])):
+ existing_bank_account = frappe.db.exists("Bank Account", {
+ 'account_name': account["name"],
+ 'bank': bank["bank_name"]
+ })
+
+ if not existing_bank_account:
try:
new_account = frappe.get_doc({
"doctype": "Bank Account",
@@ -102,10 +108,27 @@ def add_bank_accounts(response, bank, company):
except frappe.UniqueValidationError:
frappe.msgprint(_("Bank account {0} already exists and could not be created again").format(account["name"]))
except Exception:
- frappe.throw(frappe.get_traceback())
+ frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
+ frappe.throw(_("There was an error creating Bank Account while linking with Plaid."),
+ title=_("Plaid Link Failed"))
else:
- result.append(frappe.db.get_value("Bank Account", dict(integration_id=account["id"]), "name"))
+ try:
+ existing_account = frappe.get_doc('Bank Account', existing_bank_account)
+ existing_account.update({
+ "bank": bank["bank_name"],
+ "account_name": account["name"],
+ "account_type": account.get("type", ""),
+ "account_subtype": account.get("subtype", ""),
+ "mask": account.get("mask", ""),
+ "integration_id": account["id"]
+ })
+ existing_account.save()
+ result.append(existing_bank_account)
+ except Exception:
+ frappe.log_error(frappe.get_traceback(), title=_("Plaid Link Error"))
+ frappe.throw(_("There was an error updating Bank Account {} while linking with Plaid.").format(
+ existing_bank_account), title=_("Plaid Link Failed"))
return result
@@ -174,9 +197,16 @@ def get_transactions(bank, bank_account=None, start_date=None, end_date=None):
account_id = None
plaid = PlaidConnector(access_token)
- transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
- return transactions
+ try:
+ transactions = plaid.get_transactions(start_date=start_date, end_date=end_date, account_id=account_id)
+ except ItemError as e:
+ if e.code == "ITEM_LOGIN_REQUIRED":
+ msg = _("There was an error syncing transactions.") + " "
+ msg += _("Please refresh or reset the Plaid linking of the Bank {}.").format(bank) + " "
+ frappe.log_error(msg, title=_("Plaid Link Refresh Required"))
+
+ return transactions or []
def new_bank_transaction(transaction):
diff --git a/erpnext/hr/doctype/attendance/attendance.json b/erpnext/hr/doctype/attendance/attendance.json
index 20974a0b7d2..fb102a4aa17 100644
--- a/erpnext/hr/doctype/attendance/attendance.json
+++ b/erpnext/hr/doctype/attendance/attendance.json
@@ -78,7 +78,7 @@
"search_index": 1
},
{
- "depends_on": "eval:doc.status==\"On Leave\"",
+ "depends_on": "eval:doc.status==\"On Leave\" || doc.status==\"Half Day\"",
"fieldname": "leave_type",
"fieldtype": "Link",
"in_standard_filter": 1,
@@ -174,7 +174,7 @@
"icon": "fa fa-ok",
"idx": 1,
"is_submittable": 1,
- "modified": "2020-02-19 14:25:32.945842",
+ "modified": "2021-06-30 14:42:39.162146",
"modified_by": "Administrator",
"module": "HR",
"name": "Attendance",
diff --git a/erpnext/hr/doctype/job_applicant/job_applicant_list.js b/erpnext/hr/doctype/job_applicant/job_applicant_list.js
index 3b9141ba79c..2ad0d591d8c 100644
--- a/erpnext/hr/doctype/job_applicant/job_applicant_list.js
+++ b/erpnext/hr/doctype/job_applicant/job_applicant_list.js
@@ -2,7 +2,7 @@
// MIT License. See license.txt
frappe.listview_settings['Job Applicant'] = {
- add_fields: ["company", "designation", "job_applicant", "status"],
+ add_fields: ["status"],
get_indicator: function (doc) {
if (doc.status == "Accepted") {
return [__(doc.status), "green", "status,=," + doc.status];
diff --git a/erpnext/hr/doctype/loan/loan.js b/erpnext/hr/doctype/loan/loan.js
index 3f5c30c4758..97504980225 100644
--- a/erpnext/hr/doctype/loan/loan.js
+++ b/erpnext/hr/doctype/loan/loan.js
@@ -15,6 +15,15 @@ frappe.ui.form.on('Loan', {
};
});
+ frm.set_query("loan_type", function () {
+ return {
+ "filters": {
+ "docstatus": 1,
+ "company": frm.doc.company
+ }
+ };
+ });
+
frm.set_query("interest_income_account", function () {
return {
"filters": {
diff --git a/erpnext/hr/doctype/loan_application/loan_application.js b/erpnext/hr/doctype/loan_application/loan_application.js
index a73b62a894e..e150a220b86 100644
--- a/erpnext/hr/doctype/loan_application/loan_application.js
+++ b/erpnext/hr/doctype/loan_application/loan_application.js
@@ -5,8 +5,15 @@
frappe.ui.form.on('Loan Application', {
refresh: function(frm) {
- frm.trigger("toggle_fields")
- frm.trigger("add_toolbar_buttons")
+ frm.trigger("toggle_fields");
+ frm.trigger("add_toolbar_buttons");
+ frm.set_query('loan_type', () => {
+ return {
+ filters: {
+ company: frm.doc.company
+ }
+ };
+ });
},
repayment_method: function(frm) {
frm.doc.repayment_amount = frm.doc.repayment_periods = ""
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.py b/erpnext/hr/doctype/salary_slip/salary_slip.py
index 6e6ae4351c8..2213fa6fb57 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.py
@@ -869,8 +869,8 @@ class SalarySlip(TransactionBase):
`tabRepayment Schedule` as rps, `tabLoan` as l
where
l.name = rps.parent and rps.payment_date between %s and %s and
- l.repay_from_salary = 1 and l.docstatus = 1 and l.applicant = %s""",
- (self.start_date, self.end_date, self.employee), as_dict=True) or []
+ l.repay_from_salary = 1 and l.docstatus = 1 and l.applicant = %s and l.company = %s""",
+ (self.start_date, self.end_date, self.employee, self.company), as_dict=True) or []
def update_salary_slip_in_additional_salary(self):
salary_slip = self.name if self.docstatus==1 else None
diff --git a/erpnext/hr/doctype/staffing_plan/staffing_plan.py b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
index 595bcaa8d4a..c2a25d84e9d 100644
--- a/erpnext/hr/doctype/staffing_plan/staffing_plan.py
+++ b/erpnext/hr/doctype/staffing_plan/staffing_plan.py
@@ -40,7 +40,7 @@ class StaffingPlan(Document):
detail.current_openings = designation_counts['job_openings']
if detail.number_of_positions > 0:
- if detail.vacancies > 0 and detail.estimated_cost_per_position:
+ if detail.vacancies and detail.estimated_cost_per_position:
detail.total_estimated_cost = cint(detail.vacancies) * flt(detail.estimated_cost_per_position)
self.total_estimated_budget += detail.total_estimated_cost
@@ -57,8 +57,7 @@ class StaffingPlan(Document):
and sp.to_date >= %s and sp.from_date <= %s and sp.company = %s
""", (staffing_plan_detail.designation, self.from_date, self.to_date, self.company))
if overlap and overlap [0][0]:
- frappe.throw(_("Staffing Plan {0} already exist for designation {1}"
- .format(overlap[0][0], staffing_plan_detail.designation)))
+ frappe.throw(_("Staffing Plan {0} already exist for designation {1}").format(overlap[0][0], staffing_plan_detail.designation))
def validate_with_parent_plan(self, staffing_plan_detail):
if not frappe.get_cached_value('Company', self.company, "parent_company"):
@@ -75,12 +74,12 @@ class StaffingPlan(Document):
if cint(staffing_plan_detail.vacancies) > cint(parent_plan_details[0].vacancies) or \
flt(staffing_plan_detail.total_estimated_cost) > flt(parent_plan_details[0].total_estimated_cost):
frappe.throw(_("You can only plan for upto {0} vacancies and budget {1} \
- for {2} as per staffing plan {3} for parent company {4}."
- .format(cint(parent_plan_details[0].vacancies),
+ for {2} as per staffing plan {3} for parent company {4}.").format(
+ cint(parent_plan_details[0].vacancies),
parent_plan_details[0].total_estimated_cost,
frappe.bold(staffing_plan_detail.designation),
parent_plan_details[0].name,
- parent_company)), ParentCompanyError)
+ parent_company), ParentCompanyError)
#Get vacanices already planned for all companies down the hierarchy of Parent Company
lft, rgt = frappe.get_cached_value('Company', parent_company, ["lft", "rgt"])
@@ -97,14 +96,14 @@ class StaffingPlan(Document):
(flt(parent_plan_details[0].total_estimated_cost) < \
(flt(staffing_plan_detail.total_estimated_cost) + flt(all_sibling_details.total_estimated_cost))):
frappe.throw(_("{0} vacancies and {1} budget for {2} already planned for subsidiary companies of {3}. \
- You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}."
- .format(cint(all_sibling_details.vacancies),
+ You can only plan for upto {4} vacancies and and budget {5} as per staffing plan {6} for parent company {3}.").format(
+ cint(all_sibling_details.vacancies),
all_sibling_details.total_estimated_cost,
frappe.bold(staffing_plan_detail.designation),
parent_company,
cint(parent_plan_details[0].vacancies),
parent_plan_details[0].total_estimated_cost,
- parent_plan_details[0].name)))
+ parent_plan_details[0].name))
def validate_with_subsidiary_plans(self, staffing_plan_detail):
#Valdate this plan with all child company plan
@@ -120,11 +119,11 @@ class StaffingPlan(Document):
cint(staffing_plan_detail.vacancies) < cint(children_details.vacancies) or \
flt(staffing_plan_detail.total_estimated_cost) < flt(children_details.total_estimated_cost):
frappe.throw(_("Subsidiary companies have already planned for {1} vacancies at a budget of {2}. \
- Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies"
- .format(self.company,
+ Staffing Plan for {0} should allocate more vacancies and budget for {3} than planned for its subsidiary companies").format(
+ self.company,
cint(children_details.vacancies),
children_details.total_estimated_cost,
- frappe.bold(staffing_plan_detail.designation))), SubsidiaryCompanyError)
+ frappe.bold(staffing_plan_detail.designation)), SubsidiaryCompanyError)
@frappe.whitelist()
def get_designation_counts(designation, company):
diff --git a/erpnext/regional/india/e_invoice/einvoice.js b/erpnext/regional/india/e_invoice/einvoice.js
index cda732b0fb7..6ee57520495 100644
--- a/erpnext/regional/india/e_invoice/einvoice.js
+++ b/erpnext/regional/india/e_invoice/einvoice.js
@@ -1,6 +1,8 @@
erpnext.setup_einvoice_actions = (doctype) => {
frappe.ui.form.on(doctype, {
async refresh(frm) {
+ if (frm.doc.docstatus == 2) return;
+
const res = await frappe.call({
method: 'erpnext.regional.india.e_invoice.utils.validate_eligibility',
args: { doc: frm.doc }
@@ -115,7 +117,7 @@ erpnext.setup_einvoice_actions = (doctype) => {
if (irn && ewaybill && !irn_cancelled && !eway_bill_cancelled) {
const action = () => {
- let message = __('Cancellation of e-way bill is currently not supported. ');
+ let message = __('Cancellation of e-way bill is currently not supported.') + ' ';
message += '
';
message += __('You must first use the portal to cancel the e-way bill and then update the cancelled status in the ERPNext system.');
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index c9f0b0b2592..f788ce9a8d5 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -38,9 +38,13 @@ def validate_eligibility(doc):
invalid_company = not frappe.db.get_value('E Invoice User', { 'company': doc.get('company') })
invalid_supply_type = doc.get('gst_category') not in ['Registered Regular', 'SEZ', 'Overseas', 'Deemed Export']
company_transaction = doc.get('billing_address_gstin') == doc.get('company_gstin')
- no_taxes_applied = not doc.get('taxes')
- if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied:
+ # if export invoice, then taxes can be empty
+ # invoice can only be ineligible if no taxes applied and is not an export invoice
+ no_taxes_applied = not doc.get('taxes') and not doc.get('gst_category') == 'Overseas'
+ has_non_gst_item = any(d for d in doc.get('items', []) if d.get('is_non_gst'))
+
+ if invalid_company or invalid_supply_type or company_transaction or no_taxes_applied or has_non_gst_item:
return False
return True
@@ -191,18 +195,14 @@ def get_item_list(invoice):
item.qty = abs(item.qty)
- if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
- item.discount_amount = abs(item.base_amount - item.base_net_amount)
- else:
- item.discount_amount = 0
-
- item.unit_rate = abs((abs(item.taxable_value) - item.discount_amount)/ item.qty)
- item.gross_amount = abs(item.taxable_value) + item.discount_amount
+ item.unit_rate = abs(item.taxable_value / item.qty)
+ item.gross_amount = abs(item.taxable_value)
item.taxable_value = abs(item.taxable_value)
+ item.discount_amount = 0
item.batch_expiry_date = frappe.db.get_value('Batch', d.batch_no, 'expiry_date') if d.batch_no else None
item.batch_expiry_date = format_date(item.batch_expiry_date, 'dd/mm/yyyy') if item.batch_expiry_date else None
- item.is_service_item = 'N' if frappe.db.get_value('Item', d.item_code, 'is_stock_item') else 'Y'
+ item.is_service_item = 'Y' if item.gst_hsn_code and item.gst_hsn_code[:2] == "99" else 'N'
item.serial_no = ""
item = update_item_taxes(invoice, item)
@@ -254,18 +254,8 @@ def update_item_taxes(invoice, item):
def get_invoice_value_details(invoice):
invoice_value_details = frappe._dict(dict())
-
- if invoice.apply_discount_on == 'Net Total' and invoice.discount_amount:
- # Discount already applied on net total which means on items
- invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
- invoice_value_details.invoice_discount_amt = 0
- elif invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount:
- invoice_value_details.invoice_discount_amt = invoice.base_discount_amount
- invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
- else:
- invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
- # since tax already considers discount amount
- invoice_value_details.invoice_discount_amt = 0
+ invoice_value_details.base_total = abs(sum([i.taxable_value for i in invoice.get('items')]))
+ invoice_value_details.invoice_discount_amt = 0
invoice_value_details.round_off = invoice.base_rounding_adjustment
invoice_value_details.base_grand_total = abs(invoice.base_rounded_total) or abs(invoice.base_grand_total)
@@ -287,8 +277,8 @@ def update_invoice_taxes(invoice, invoice_value_details):
considered_rows = []
for t in invoice.taxes:
- tax_amount = t.base_tax_amount if (invoice.apply_discount_on == 'Grand Total' and invoice.discount_amount) \
- else t.base_tax_amount_after_discount_amount
+ tax_amount = t.base_tax_amount_after_discount_amount
+
if t.account_head in gst_accounts_list:
if t.account_head in gst_accounts.cess_account:
# using after discount amt since item also uses after discount amt for cess calc
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 7b28d21a45b..5685c174737 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -856,12 +856,8 @@ def update_taxable_values(doc, method):
considered_rows.append(prev_row_id)
for item in doc.get('items'):
- if doc.apply_discount_on == 'Grand Total' and doc.discount_amount:
- proportionate_value = item.base_amount if doc.base_total else item.qty
- total_value = doc.base_total if doc.base_total else doc.total_qty
- else:
- proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
- total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
+ proportionate_value = item.base_net_amount if doc.base_net_total else item.qty
+ total_value = doc.base_net_total if doc.base_net_total else doc.total_qty
applicable_charges = flt(flt(proportionate_value * (flt(additional_taxes) / flt(total_value)),
item.precision('taxable_value')))
diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js
index 1ccd8cf31a0..9ce2125065a 100644
--- a/erpnext/stock/doctype/material_request/material_request.js
+++ b/erpnext/stock/doctype/material_request/material_request.js
@@ -69,7 +69,8 @@ frappe.ui.form.on('Material Request', {
}
if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') {
- if (flt(frm.doc.per_ordered, 2) < 100) {
+ let precision = frappe.defaults.get_default("float_precision");
+ if (flt(frm.doc.per_ordered, precision) < 100) {
let add_create_pick_list_button = () => {
frm.add_custom_button(__('Pick List'),
() => frm.events.create_pick_list(frm), __('Create'));
diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js
index 614ecb8a8f9..31c5afe3f5f 100644
--- a/erpnext/stock/doctype/material_request/material_request_list.js
+++ b/erpnext/stock/doctype/material_request/material_request_list.js
@@ -1,16 +1,17 @@
frappe.listview_settings['Material Request'] = {
add_fields: ["material_request_type", "status", "per_ordered", "per_received"],
get_indicator: function(doc) {
- if(doc.status=="Stopped") {
+ var precision = frappe.defaults.get_default("float_precision");
+ if (doc.status=="Stopped") {
return [__("Stopped"), "red", "status,=,Stopped"];
- } else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) {
+ } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 0) {
return [__("Pending"), "orange", "per_ordered,=,0"];
- } else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) {
+ } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) < 100) {
return [__("Partially ordered"), "yellow", "per_ordered,<,100"];
- } else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) {
- if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) {
+ } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) {
+ if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) < 100 && flt(doc.per_received, precision) > 0) {
return [__("Partially Received"), "yellow", "per_received,<,100"];
- } else if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) == 100) {
+ } else if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) == 100) {
return [__("Received"), "green", "per_received,=,100"];
} else if (doc.material_request_type == "Purchase") {
return [__("Ordered"), "green", "per_ordered,=,100"];
diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html
index 80a542f74bf..553ae2ac61c 100644
--- a/erpnext/templates/includes/transaction_row.html
+++ b/erpnext/templates/includes/transaction_row.html
@@ -13,9 +13,11 @@
{{ doc.items_preview }}
-