Merge pull request #24189 from timgates42/bugfix_typo_update
docs: fix simple typo, udpate -> update
diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py
index 0f115f9..cd88b11 100644
--- a/erpnext/accounts/doctype/budget/test_budget.py
+++ b/erpnext/accounts/doctype/budget/test_budget.py
@@ -159,10 +159,10 @@
budget = make_budget(budget_against="Cost Center")
month = now_datetime().month
- if month > 10:
- month = 10
+ if month > 9:
+ month = 9
- for i in range(month):
+ for i in range(month+1):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True)
@@ -181,10 +181,10 @@
budget = make_budget(budget_against="Project")
month = now_datetime().month
- if month > 10:
- month = 10
+ if month > 9:
+ month = 9
- for i in range(month):
+ for i in range(month + 1):
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True, project="_Test Project")
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index d486ff6..ac98dcc 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -267,6 +267,8 @@
from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
if not self.pos_profile:
pos_profile = get_pos_profile(self.company) or {}
+ if not pos_profile:
+ frappe.throw(_("No POS Profile found. Please create a New POS Profile first"))
self.pos_profile = pos_profile.get('name')
profile = {}
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 40009ac..50eb400 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe, erpnext
import frappe.defaults
-from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate, get_link_to_form
+from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date
from frappe.model.mapper import get_mapped_doc
@@ -549,7 +549,12 @@
self.against_income_account = ','.join(against_acc)
def add_remarks(self):
- if not self.remarks: self.remarks = 'No Remarks'
+ if not self.remarks:
+ if self.po_no and self.po_date:
+ self.remarks = _("Against Customer Order {0} dated {1}").format(self.po_no,
+ formatdate(self.po_date))
+ else:
+ self.remarks = _("No Remarks")
def validate_auto_set_posting_time(self):
# Don't auto set the posting date and time if invoice is amended
diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
index e17973c..ba8535a 100644
--- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js
+++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js
@@ -75,62 +75,70 @@
return Object.assign(options, {
checkboxColumn: true,
events: {
- onCheckRow: function(data) {
+ onCheckRow: function (data) {
+ if (!data) return;
+
+ const data_doctype = $(
+ data[2].html
+ )[0].attributes.getNamedItem("data-doctype").value;
+ const tree_type = frappe.query_report.filters[0].value;
+ if (data_doctype != tree_type) return;
+
row_name = data[2].content;
length = data.length;
- var tree_type = frappe.query_report.filters[0].value;
-
- if(tree_type == "Supplier" || tree_type == "Item") {
- row_values = data.slice(4,length-1).map(function (column) {
- return column.content;
- })
- }
- else {
- row_values = data.slice(3,length-1).map(function (column) {
- return column.content;
- })
+ if (tree_type == "Supplier") {
+ row_values = data
+ .slice(4, length - 1)
+ .map(function (column) {
+ return column.content;
+ });
+ } else if (tree_type == "Item") {
+ row_values = data
+ .slice(5, length - 1)
+ .map(function (column) {
+ return column.content;
+ });
+ } else {
+ row_values = data
+ .slice(3, length - 1)
+ .map(function (column) {
+ return column.content;
+ });
}
- entry = {
- 'name':row_name,
- 'values':row_values
- }
+ entry = {
+ name: row_name,
+ values: row_values,
+ };
let raw_data = frappe.query_report.chart.data;
let new_datasets = raw_data.datasets;
- var found = false;
-
- for(var i=0; i < new_datasets.length;i++){
- if(new_datasets[i].name == row_name){
- found = true;
- new_datasets.splice(i,1);
- break;
+ let element_found = new_datasets.some((element, index, array)=>{
+ if(element.name == row_name){
+ array.splice(index, 1)
+ return true
}
- }
+ return false
+ })
- if(!found){
+ if (!element_found) {
new_datasets.push(entry);
}
-
let new_data = {
labels: raw_data.labels,
- datasets: new_datasets
- }
-
- setTimeout(() => {
- frappe.query_report.chart.update(new_data)
- },500)
-
-
- setTimeout(() => {
- frappe.query_report.chart.draw(true);
- }, 1000)
+ datasets: new_datasets,
+ };
+ chart_options = {
+ data: new_data,
+ type: "line",
+ };
+ frappe.query_report.render_chart(chart_options);
frappe.query_report.raw_chart_data = new_data;
},
- }
+ },
});
}
}
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index 7979226..a048d6e 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -328,6 +328,7 @@
target_doc.po_detail = source_doc.po_detail
target_doc.pr_detail = source_doc.pr_detail
target_doc.purchase_invoice_item = source_doc.name
+ target_doc.price_list_rate = 0
elif doctype == "Delivery Note":
returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype)
@@ -353,6 +354,7 @@
target_doc.dn_detail = source_doc.dn_detail
target_doc.expense_account = source_doc.expense_account
target_doc.sales_invoice_item = source_doc.name
+ target_doc.price_list_rate = 0
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 85cfb95..812021f 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -233,7 +233,7 @@
'allow_zero_valuation': d.allow_zero_valuation_rate,
'sales_invoice_item': d.get("sales_invoice_item"),
'dn_detail': d.get("dn_detail"),
- 'incoming_rate': p.incoming_rate
+ 'incoming_rate': p.get("incoming_rate")
}))
else:
il.append(frappe._dict({
@@ -252,7 +252,7 @@
'allow_zero_valuation': d.allow_zero_valuation_rate,
'sales_invoice_item': d.get("sales_invoice_item"),
'dn_detail': d.get("dn_detail"),
- 'incoming_rate': d.incoming_rate
+ 'incoming_rate': d.get("incoming_rate")
}))
return il
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
index 0104386..b33c326 100644
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
+++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
@@ -17,6 +17,8 @@
"enable_free_follow_ups",
"max_visits",
"valid_days",
+ "inpatient_settings_section",
+ "allow_discharge_despite_unbilled_services",
"healthcare_service_items",
"inpatient_visit_charge_item",
"op_consulting_charge_item",
@@ -302,11 +304,22 @@
"fieldname": "enable_free_follow_ups",
"fieldtype": "Check",
"label": "Enable Free Follow-ups"
+ },
+ {
+ "fieldname": "inpatient_settings_section",
+ "fieldtype": "Section Break",
+ "label": "Inpatient Settings"
+ },
+ {
+ "default": "0",
+ "fieldname": "allow_discharge_despite_unbilled_services",
+ "fieldtype": "Check",
+ "label": "Allow Discharge Despite Unbilled Healthcare Services"
}
],
"issingle": 1,
"links": [],
- "modified": "2020-07-08 15:17:21.543218",
+ "modified": "2021-01-04 10:19:22.329272",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Healthcare Settings",
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
index bc76970..dc549a6 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.py
@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe, json
from frappe import _
-from frappe.utils import today, now_datetime, getdate, get_datetime
+from frappe.utils import today, now_datetime, getdate, get_datetime, get_link_to_form
from frappe.model.document import Document
from frappe.desk.reportview import get_match_cond
@@ -113,6 +113,7 @@
inpatient_record.status = 'Admission Scheduled'
inpatient_record.save(ignore_permissions = True)
+
@frappe.whitelist()
def schedule_discharge(args):
discharge_order = json.loads(args)
@@ -126,16 +127,19 @@
frappe.db.set_value('Patient', discharge_order['patient'], 'inpatient_status', inpatient_record.status)
frappe.db.set_value('Patient Encounter', inpatient_record.discharge_encounter, 'inpatient_status', inpatient_record.status)
+
def set_details_from_ip_order(inpatient_record, ip_order):
for key in ip_order:
inpatient_record.set(key, ip_order[key])
+
def set_ip_child_records(inpatient_record, inpatient_record_child, encounter_child):
for item in encounter_child:
table = inpatient_record.append(inpatient_record_child)
for df in table.meta.get('fields'):
table.set(df.fieldname, item.get(df.fieldname))
+
def check_out_inpatient(inpatient_record):
if inpatient_record.inpatient_occupancies:
for inpatient_occupancy in inpatient_record.inpatient_occupancies:
@@ -144,54 +148,88 @@
inpatient_occupancy.check_out = now_datetime()
frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant")
+
def discharge_patient(inpatient_record):
- validate_invoiced_inpatient(inpatient_record)
+ validate_inpatient_invoicing(inpatient_record)
inpatient_record.discharge_date = today()
inpatient_record.status = "Discharged"
inpatient_record.save(ignore_permissions = True)
-def validate_invoiced_inpatient(inpatient_record):
- pending_invoices = []
+
+def validate_inpatient_invoicing(inpatient_record):
+ if frappe.db.get_single_value("Healthcare Settings", "allow_discharge_despite_unbilled_services"):
+ return
+
+ pending_invoices = get_pending_invoices(inpatient_record)
+
+ if pending_invoices:
+ message = _("Cannot mark Inpatient Record as Discharged since there are unbilled services. ")
+
+ formatted_doc_rows = ''
+
+ for doctype, docnames in pending_invoices.items():
+ formatted_doc_rows += """
+ <td>{0}</td>
+ <td>{1}</td>
+ </tr>""".format(doctype, docnames)
+
+ message += """
+ <table class='table'>
+ <thead>
+ <th>{0}</th>
+ <th>{1}</th>
+ </thead>
+ {2}
+ </table>
+ """.format(_("Healthcare Service"), _("Documents"), formatted_doc_rows)
+
+ frappe.throw(message, title=_("Unbilled Services"), is_minimizable=True, wide=True)
+
+
+def get_pending_invoices(inpatient_record):
+ pending_invoices = {}
if inpatient_record.inpatient_occupancies:
service_unit_names = False
for inpatient_occupancy in inpatient_record.inpatient_occupancies:
- if inpatient_occupancy.invoiced != 1:
+ if not inpatient_occupancy.invoiced:
if service_unit_names:
service_unit_names += ", " + inpatient_occupancy.service_unit
else:
service_unit_names = inpatient_occupancy.service_unit
if service_unit_names:
- pending_invoices.append("Inpatient Occupancy (" + service_unit_names + ")")
+ pending_invoices["Inpatient Occupancy"] = service_unit_names
docs = ["Patient Appointment", "Patient Encounter", "Lab Test", "Clinical Procedure"]
for doc in docs:
- doc_name_list = get_inpatient_docs_not_invoiced(doc, inpatient_record)
+ doc_name_list = get_unbilled_inpatient_docs(doc, inpatient_record)
if doc_name_list:
pending_invoices = get_pending_doc(doc, doc_name_list, pending_invoices)
- if pending_invoices:
- frappe.throw(_("Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}").format(", "
- .join(pending_invoices)), title=_('Unbilled Invoices'))
+ return pending_invoices
+
def get_pending_doc(doc, doc_name_list, pending_invoices):
if doc_name_list:
doc_ids = False
for doc_name in doc_name_list:
+ doc_link = get_link_to_form(doc, doc_name.name)
if doc_ids:
- doc_ids += ", "+doc_name.name
+ doc_ids += ", " + doc_link
else:
- doc_ids = doc_name.name
+ doc_ids = doc_link
if doc_ids:
- pending_invoices.append(doc + " (" + doc_ids + ")")
+ pending_invoices[doc] = doc_ids
return pending_invoices
-def get_inpatient_docs_not_invoiced(doc, inpatient_record):
+
+def get_unbilled_inpatient_docs(doc, inpatient_record):
return frappe.db.get_list(doc, filters = {'patient': inpatient_record.patient,
'inpatient_record': inpatient_record.name, 'docstatus': 1, 'invoiced': 0})
+
def admit_patient(inpatient_record, service_unit, check_in, expected_discharge=None):
inpatient_record.admitted_datetime = check_in
inpatient_record.status = 'Admitted'
@@ -203,6 +241,7 @@
frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_status', 'Admitted')
frappe.db.set_value('Patient', inpatient_record.patient, 'inpatient_record', inpatient_record.name)
+
def transfer_patient(inpatient_record, service_unit, check_in):
item_line = inpatient_record.append('inpatient_occupancies', {})
item_line.service_unit = service_unit
@@ -212,6 +251,7 @@
frappe.db.set_value("Healthcare Service Unit", service_unit, "occupancy_status", "Occupied")
+
def patient_leave_service_unit(inpatient_record, check_out, leave_from):
if inpatient_record.inpatient_occupancies:
for inpatient_occupancy in inpatient_record.inpatient_occupancies:
@@ -221,6 +261,7 @@
frappe.db.set_value("Healthcare Service Unit", inpatient_occupancy.service_unit, "occupancy_status", "Vacant")
inpatient_record.save(ignore_permissions = True)
+
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_leave_from(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
index 70706ad..e8a9444 100644
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
@@ -40,6 +40,31 @@
self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record"))
self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status"))
+ def test_allow_discharge_despite_unbilled_services(self):
+ frappe.db.sql("""delete from `tabInpatient Record`""")
+ setup_inpatient_settings()
+ patient = create_patient()
+ # Schedule Admission
+ ip_record = create_inpatient(patient)
+ ip_record.expected_length_of_stay = 0
+ ip_record.save(ignore_permissions = True)
+
+ # Admit
+ service_unit = get_healthcare_service_unit()
+ admit_patient(ip_record, service_unit, now_datetime())
+
+ # Discharge
+ schedule_discharge(frappe.as_json({"patient": patient}))
+ self.assertEqual("Vacant", frappe.db.get_value("Healthcare Service Unit", service_unit, "occupancy_status"))
+
+ ip_record = frappe.get_doc("Inpatient Record", ip_record.name)
+ # Should not validate Pending Invoices
+ ip_record.discharge()
+
+ self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_record"))
+ self.assertEqual(None, frappe.db.get_value("Patient", patient, "inpatient_status"))
+
+
def test_validate_overlap_admission(self):
frappe.db.sql("""delete from `tabInpatient Record`""")
patient = create_patient()
@@ -63,6 +88,13 @@
inpatient_occupancy.invoiced = 1
ip_record.save(ignore_permissions = True)
+
+def setup_inpatient_settings():
+ settings = frappe.get_single("Healthcare Settings")
+ settings.allow_discharge_despite_unbilled_services = 1
+ settings.save()
+
+
def create_inpatient(patient):
patient_obj = frappe.get_doc('Patient', patient)
inpatient_record = frappe.new_doc('Inpatient Record')
@@ -78,6 +110,7 @@
inpatient_record.scheduled_date = today()
return inpatient_record
+
def get_healthcare_service_unit():
service_unit = get_random("Healthcare Service Unit", filters={"inpatient_occupancy": 1})
if not service_unit:
@@ -105,6 +138,7 @@
return service_unit.name
return service_unit
+
def get_service_unit_type():
service_unit_type = get_random("Healthcare Service Unit Type", filters={"inpatient_occupancy": 1})
@@ -116,6 +150,7 @@
return service_unit_type.name
return service_unit_type
+
def create_patient():
patient = frappe.db.exists('Patient', '_Test IPD Patient')
if not patient:
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index 4f1c04f..dc2aaa4 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -813,7 +813,7 @@
"idx": 24,
"image_field": "image",
"links": [],
- "modified": "2020-10-16 15:02:04.283657",
+ "modified": "2021-01-01 16:54:33.477439",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",
@@ -855,7 +855,6 @@
"write": 1
}
],
- "quick_entry": 1,
"search_fields": "employee_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
diff --git a/erpnext/hr/doctype/leave_allocation/leave_allocation.json b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
index 4b31501..3a300c0 100644
--- a/erpnext/hr/doctype/leave_allocation/leave_allocation.json
+++ b/erpnext/hr/doctype/leave_allocation/leave_allocation.json
@@ -11,6 +11,7 @@
"employee",
"employee_name",
"department",
+ "company",
"column_break1",
"leave_type",
"from_date",
@@ -219,6 +220,15 @@
"label": "Leave Policy Assignment",
"options": "Leave Policy Assignment",
"read_only": 1
+ },
+ {
+ "fetch_from": "employee.company",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1,
+ "reqd": 1
}
],
"icon": "fa fa-ok",
@@ -226,7 +236,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-08-20 14:25:10.314323",
+ "modified": "2021-01-04 18:46:13.184104",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Allocation",
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
index 4abba5f..d74760a 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"creation": "2019-05-09 15:47:39.760406",
"doctype": "DocType",
"engine": "InnoDB",
@@ -8,6 +9,7 @@
"leave_type",
"transaction_type",
"transaction_name",
+ "company",
"leaves",
"column_break_7",
"from_date",
@@ -106,12 +108,22 @@
"fieldtype": "Link",
"label": "Holiday List",
"options": "Holiday List"
+ },
+ {
+ "fetch_from": "employee.company",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "label": "Company",
+ "options": "Company",
+ "read_only": 1,
+ "reqd": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
- "modified": "2020-09-04 12:16:36.569066",
+ "links": [],
+ "modified": "2021-01-04 18:47:45.146652",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Ledger Entry",
diff --git a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json
index bbb4222..a0327bd 100644
--- a/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json
+++ b/erpnext/hr/doctype/leave_policy_assignment/leave_policy_assignment.json
@@ -111,13 +111,14 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2020-12-17 16:27:20.311060",
+ "modified": "2020-12-31 16:43:30.695206",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Policy Assignment",
"owner": "Administrator",
"permissions": [
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -131,6 +132,7 @@
"write": 1
},
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -144,6 +146,7 @@
"write": 1
},
{
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
diff --git a/erpnext/loan_management/doctype/loan/loan.py b/erpnext/loan_management/doctype/loan/loan.py
index cd40a66..2e0a4d1 100644
--- a/erpnext/loan_management/doctype/loan/loan.py
+++ b/erpnext/loan_management/doctype/loan/loan.py
@@ -6,6 +6,7 @@
import frappe, math, json
import erpnext
from frappe import _
+from six import string_types
from frappe.utils import flt, rounded, add_months, nowdate, getdate, now_datetime
from erpnext.loan_management.doctype.loan_security_unpledge.loan_security_unpledge import get_pledged_security_qty
from erpnext.controllers.accounts_controller import AccountsController
@@ -280,10 +281,13 @@
return write_off
@frappe.whitelist()
-def unpledge_security(loan=None, loan_security_pledge=None, as_dict=0, save=0, submit=0, approve=0):
- # if loan is passed it will be considered as full unpledge
+def unpledge_security(loan=None, loan_security_pledge=None, security_map=None, as_dict=0, save=0, submit=0, approve=0):
+ # if no security_map is passed it will be considered as full unpledge
+ if security_map and isinstance(security_map, string_types):
+ security_map = json.loads(security_map)
+
if loan:
- pledge_qty_map = get_pledged_security_qty(loan)
+ pledge_qty_map = security_map or get_pledged_security_qty(loan)
loan_doc = frappe.get_doc('Loan', loan)
unpledge_request = create_loan_security_unpledge(pledge_qty_map, loan_doc.name, loan_doc.company,
loan_doc.applicant_type, loan_doc.applicant)
diff --git a/erpnext/loan_management/doctype/loan/test_loan.py b/erpnext/loan_management/doctype/loan/test_loan.py
index 1d831ae..2abd7d8 100644
--- a/erpnext/loan_management/doctype/loan/test_loan.py
+++ b/erpnext/loan_management/doctype/loan/test_loan.py
@@ -325,6 +325,64 @@
self.assertEquals(amounts['payable_principal_amount'], 0.0)
self.assertEqual(amounts['interest_amount'], 0)
+ def test_partial_loan_security_unpledge(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 2000.00
+ },
+ {
+ "loan_security": "Test Security 2",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ first_date = '2019-10-01'
+ last_date = '2019-10-30'
+
+ make_loan_disbursement_entry(loan.name, loan.loan_amount, disbursement_date=first_date)
+ process_loan_interest_accrual_for_demand_loans(posting_date = last_date)
+
+ repayment_entry = create_repayment_entry(loan.name, self.applicant2, add_days(last_date, 5), 600000)
+ repayment_entry.submit()
+
+ unpledge_map = {'Test Security 2': 2000}
+
+ unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
+ unpledge_request.submit()
+ unpledge_request.status = 'Approved'
+ unpledge_request.save()
+ unpledge_request.submit()
+ unpledge_request.load_from_db()
+ self.assertEqual(unpledge_request.docstatus, 1)
+
+ def test_santined_loan_security_unpledge(self):
+ pledge = [{
+ "loan_security": "Test Security 1",
+ "qty": 4000.00
+ }]
+
+ loan_application = create_loan_application('_Test Company', self.applicant2, 'Demand Loan', pledge)
+ create_pledge(loan_application)
+
+ loan = create_demand_loan(self.applicant2, "Demand Loan", loan_application, posting_date='2019-10-01')
+ loan.submit()
+
+ self.assertEquals(loan.loan_amount, 1000000)
+
+ unpledge_map = {'Test Security 1': 4000}
+ unpledge_request = unpledge_security(loan=loan.name, security_map = unpledge_map, save=1)
+ unpledge_request.submit()
+ unpledge_request.status = 'Approved'
+ unpledge_request.save()
+ unpledge_request.submit()
+
def test_disbursal_check_with_shortfall(self):
pledges = [{
"loan_security": "Test Security 2",
diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
index 8ec0bfb..6469806 100644
--- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
+++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.py
@@ -81,7 +81,6 @@
process_loan_security_shortfall)
def create_loan_security_shortfall(loan, loan_amount, security_value, shortfall_amount, process_loan_security_shortfall):
-
existing_shortfall = frappe.db.get_value("Loan Security Shortfall", {"loan": loan, "status": "Pending"}, "name")
if existing_shortfall:
diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
index c29f325..c4c2d68 100644
--- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
+++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.py
@@ -30,6 +30,8 @@
d.idx, frappe.bold(d.loan_security)))
def validate_unpledge_qty(self):
+ from erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall import get_ltv_ratio
+
pledge_qty_map = get_pledged_security_qty(self.loan)
ltv_ratio_map = frappe._dict(frappe.get_all("Loan Security Type",
@@ -42,11 +44,19 @@
"valid_upto": (">=", get_datetime())
}, as_list=1))
- total_payment, principal_paid, interest_payable, written_off_amount = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
- 'total_interest_payable', 'written_off_amount'])
+ loan_details = frappe.get_value("Loan", self.loan, ['total_payment', 'total_principal_paid',
+ 'total_interest_payable', 'written_off_amount', 'disbursed_amount', 'status'], as_dict=1)
- pending_principal_amount = flt(total_payment) - flt(interest_payable) - flt(principal_paid) - flt(written_off_amount)
+ if loan_details.status == 'Disbursed':
+ pending_principal_amount = flt(loan_details.total_payment) - flt(loan_details.total_interest_payable) \
+ - flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
+ else:
+ pending_principal_amount = flt(loan_details.disbursed_amount) - flt(loan_details.total_interest_payable) \
+ - flt(loan_details.total_principal_paid) - flt(loan_details.written_off_amount)
+
security_value = 0
+ unpledge_qty_map = {}
+ ltv_ratio = 0
for security in self.securities:
pledged_qty = pledge_qty_map.get(security.loan_security, 0)
@@ -57,13 +67,15 @@
msg += _("You are trying to unpledge more.")
frappe.throw(msg, title=_("Loan Security Unpledge Error"))
- qty_after_unpledge = pledged_qty - security.qty
- ltv_ratio = ltv_ratio_map.get(security.loan_security_type)
+ unpledge_qty_map.setdefault(security.loan_security, 0)
+ unpledge_qty_map[security.loan_security] += security.qty
- current_price = loan_security_price_map.get(security.loan_security)
- if not current_price:
- frappe.throw(_("No valid Loan Security Price found for {0}").format(frappe.bold(security.loan_security)))
+ for security in pledge_qty_map:
+ if not ltv_ratio:
+ ltv_ratio = get_ltv_ratio(security)
+ qty_after_unpledge = pledge_qty_map.get(security, 0) - unpledge_qty_map.get(security, 0)
+ current_price = loan_security_price_map.get(security)
security_value += qty_after_unpledge * current_price
if not security_value and flt(pending_principal_amount, 2) > 0:
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index f2e4f72..923ed2f 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -742,3 +742,4 @@
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
erpnext.patches.v13_0.add_po_to_global_search
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
+erpnext.patches.v13_0.set_company_in_leave_ledger_entry
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
new file mode 100644
index 0000000..66857c4
--- /dev/null
+++ b/erpnext/patches/v13_0/set_company_in_leave_ledger_entry.py
@@ -0,0 +1,7 @@
+import frappe
+
+def execute():
+ frappe.reload_doc('HR', 'doctype', 'Leave Allocation')
+ frappe.reload_doc('HR', 'doctype', 'Leave Ledger Entry')
+ frappe.db.sql("""update `tabLeave Ledger Entry` as lle set company = (select company from `tabEmployee` where employee = lle.employee)""")
+ frappe.db.sql("""update `tabLeave Allocation` as la set company = (select company from `tabEmployee` where employee = la.employee)""")
\ No newline at end of file
diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py
index 561e967..8cf09aa 100644
--- a/erpnext/patches/v13_0/update_old_loans.py
+++ b/erpnext/patches/v13_0/update_old_loans.py
@@ -1,7 +1,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import nowdate
+from frappe.utils import nowdate, flt
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
from erpnext.loan_management.doctype.loan.loan import make_repayment_entry
@@ -113,15 +113,15 @@
interest_paid = 0
principal_paid = 0
- if total_interest > entry.interest_amount:
- interest_paid = entry.interest_amount
+ if flt(total_interest) > flt(entry.interest_amount):
+ interest_paid = flt(entry.interest_amount)
else:
- interest_paid = total_interest
+ interest_paid = flt(total_interest)
- if total_principal > entry.payable_principal_amount:
- principal_paid = entry.payable_principal_amount
+ if flt(total_principal) > flt(entry.payable_principal_amount):
+ principal_paid = flt(entry.payable_principal_amount)
else:
- principal_paid = total_principal
+ principal_paid = flt(total_principal)
frappe.db.sql(""" UPDATE `tabLoan Interest Accrual`
SET paid_principal_amount = `paid_principal_amount` + %s,
@@ -129,8 +129,8 @@
WHERE name = %s""",
(principal_paid, interest_paid, entry.name))
- total_principal -= principal_paid
- total_interest -= interest_paid
+ total_principal = flt(total_principal) - principal_paid
+ total_interest = flt(total_interest) - interest_paid
def create_loan_type(loan, loan_type_name, penalty_account):
loan_type_doc = frappe.new_doc('Loan Type')
diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
index 54106c8..e098ec7 100644
--- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py
@@ -22,7 +22,7 @@
frappe.db.sql("delete from `tab%s`" % dt)
make_earning_salary_component(setup=True, company_list=["_Test Company"])
- make_deduction_salary_component(setup=True, company_list=["_Test Company"])
+ make_deduction_salary_component(setup=True, test_tax=False, company_list=["_Test Company"])
frappe.db.set_value("Payroll Settings", None, "email_salary_slip_to_employee", 0)
@@ -107,9 +107,9 @@
frappe.db.get_value("Company", "_Test Company", "default_payroll_payable_account") != "_Test Payroll Payable - _TC":
frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
"_Test Payroll Payable - _TC")
-
- make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=frappe.db.get_value("Company", "_Test Company", "default_currency"))
- make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=frappe.db.get_value("Company", "_Test Company", "default_currency"))
+ currency=frappe.db.get_value("Company", "_Test Company", "default_currency")
+ make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company", currency=currency, test_tax=False)
+ make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company", currency=currency, test_tax=False)
dates = get_start_end_dates('Monthly', nowdate())
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.js b/erpnext/payroll/doctype/salary_slip/salary_slip.js
index 5694871..51fb359 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.js
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.js
@@ -151,7 +151,6 @@
var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields, false);
frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields, false);
- calculate_totals(frm);
frm.trigger("set_dynamic_labels");
},
@@ -214,14 +213,16 @@
});
var calculate_totals = function(frm) {
- if (frm.doc.earnings || frm.doc.deductions) {
- frappe.call({
- method: "set_totals",
- doc: frm.doc,
- callback: function() {
- frm.refresh_fields();
- }
- });
+ if (frm.doc.docstatus === 0) {
+ if (frm.doc.earnings || frm.doc.deductions) {
+ frappe.call({
+ method: "set_totals",
+ doc: frm.doc,
+ callback: function() {
+ frm.refresh_fields();
+ }
+ });
+ }
}
};
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index 99d8a83..47c9d31 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -143,8 +143,8 @@
self.salary_slip_based_on_timesheet = self._salary_structure_doc.salary_slip_based_on_timesheet or 0
self.set_time_sheet()
self.pull_sal_struct()
- payroll_based_on, consider_unmarked_attendance_as = frappe.db.get_value("Payroll Settings", None, ["payroll_based_on","consider_unmarked_attendance_as"])
- return [payroll_based_on, consider_unmarked_attendance_as]
+ ps = frappe.db.get_value("Payroll Settings", None, ["payroll_based_on","consider_unmarked_attendance_as"], as_dict=1)
+ return [ps.payroll_based_on, ps.consider_unmarked_attendance_as]
def set_time_sheet(self):
if self.salary_slip_based_on_timesheet:
@@ -424,16 +424,19 @@
def calculate_net_pay(self):
if self.salary_structure:
self.calculate_component_amounts("earnings")
- self.gross_pay = self.get_component_totals("earnings")
+ self.gross_pay = self.get_component_totals("earnings", depends_on_payment_days=1)
self.base_gross_pay = flt(flt(self.gross_pay) * flt(self.exchange_rate), self.precision('base_gross_pay'))
if self.salary_structure:
self.calculate_component_amounts("deductions")
- self.total_deduction = self.get_component_totals("deductions")
- self.base_total_deduction = flt(flt(self.total_deduction) * flt(self.exchange_rate), self.precision('base_total_deduction'))
self.set_loan_repayment()
+ self.set_component_amounts_based_on_payment_days()
+ self.set_net_pay()
+ def set_net_pay(self):
+ self.total_deduction = self.get_component_totals("deductions")
+ self.base_total_deduction = flt(flt(self.total_deduction) * flt(self.exchange_rate), self.precision('base_total_deduction'))
self.net_pay = flt(self.gross_pay) - (flt(self.total_deduction) + flt(self.total_loan_repayment))
self.rounded_total = rounded(self.net_pay)
self.base_net_pay = flt(flt(self.net_pay) * flt(self.exchange_rate), self.precision('base_net_pay'))
@@ -455,8 +458,6 @@
else:
self.add_tax_components(payroll_period)
- self.set_component_amounts_based_on_payment_days(component_type)
-
def add_structure_components(self, component_type):
data = self.get_data_for_eval()
for struct_row in self._salary_structure_doc.get(component_type):
@@ -813,7 +814,7 @@
cint(row.depends_on_payment_days) and cint(self.total_working_days) and
(not self.salary_slip_based_on_timesheet or
getdate(self.start_date) < joining_date or
- getdate(self.end_date) > relieving_date
+ (relieving_date and getdate(self.end_date) > relieving_date)
)):
additional_amount = flt((flt(row.additional_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), row.precision("additional_amount"))
@@ -946,15 +947,21 @@
struct_row['variable_based_on_taxable_salary'] = component.variable_based_on_taxable_salary
return struct_row
- def get_component_totals(self, component_type):
+ def get_component_totals(self, component_type, depends_on_payment_days=0):
+ joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
+ ["date_of_joining", "relieving_date"])
+
total = 0.0
for d in self.get(component_type):
if not d.do_not_include_in_total:
- d.amount = flt(d.amount, d.precision("amount"))
- total += d.amount
+ if depends_on_payment_days:
+ amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
+ else:
+ amount = flt(d.amount, d.precision("amount"))
+ total += amount
return total
- def set_component_amounts_based_on_payment_days(self, component_type):
+ def set_component_amounts_based_on_payment_days(self):
joining_date, relieving_date = frappe.get_cached_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
@@ -964,8 +971,9 @@
if not joining_date:
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
- for d in self.get(component_type):
- d.amount = self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0]
+ for component_type in ("earnings", "deductions"):
+ for d in self.get(component_type):
+ d.amount = flt(self.get_amount_based_on_payment_days(d, joining_date, relieving_date)[0], d.precision("amount"))
def set_loan_repayment(self):
self.total_loan_repayment = 0
@@ -1089,17 +1097,17 @@
self.calculate_net_pay()
def set_totals(self):
- self.gross_pay = 0
+ self.gross_pay = 0.0
if self.salary_slip_based_on_timesheet == 1:
self.calculate_total_for_salary_slip_based_on_timesheet()
else:
- self.total_deduction = 0
+ self.total_deduction = 0.0
if self.earnings:
for earning in self.earnings:
- self.gross_pay += flt(earning.amount)
+ self.gross_pay += flt(earning.amount, earning.precision("amount"))
if self.deductions:
for deduction in self.deductions:
- self.total_deduction += flt(deduction.amount)
+ self.total_deduction += flt(deduction.amount, deduction.precision("amount"))
self.net_pay = flt(self.gross_pay) - flt(self.total_deduction) - flt(self.total_loan_repayment)
self.set_base_totals()
@@ -1145,8 +1153,10 @@
fields = ['sum(net_pay) as sum'],
filters = {'employee_name' : self.employee_name,
'start_date' : ['>=', period_start_date],
- 'end_date' : ['<', period_end_date]})
-
+ 'end_date' : ['<', period_end_date],
+ 'name': ['!=', self.name],
+ 'docstatus': 1
+ })
year_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
@@ -1160,7 +1170,9 @@
fields = ['sum(net_pay) as sum'],
filters = {'employee_name' : self.employee_name,
'start_date' : ['>=', first_day_of_the_month],
- 'end_date' : ['<', self.start_date]
+ 'end_date' : ['<', self.start_date],
+ 'name': ['!=', self.name],
+ 'docstatus': 1
})
month_to_date = flt(salary_slip_sum[0].sum) if salary_slip_sum else 0.0
diff --git a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
index bb310c4..4368c03 100644
--- a/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/test_salary_slip.py
@@ -318,7 +318,7 @@
year_to_date = 0
for slip in salary_slips:
- year_to_date += slip.net_pay
+ year_to_date += flt(slip.net_pay)
self.assertEqual(slip.year_to_date, year_to_date)
def test_tax_for_payroll_period(self):
@@ -585,14 +585,6 @@
"amount": 200,
"exempted_from_income_tax": 1
- },
- {
- "salary_component": 'TDS',
- "abbr":'T',
- "type": "Deduction",
- "depends_on_payment_days": 0,
- "variable_based_on_taxable_salary": 1,
- "round_to_the_nearest_integer": 1
}
]
if not test_tax:
@@ -603,6 +595,15 @@
"type": "Deduction",
"round_to_the_nearest_integer": 1
})
+ else:
+ data.append({
+ "salary_component": 'TDS',
+ "abbr":'T',
+ "type": "Deduction",
+ "depends_on_payment_days": 0,
+ "variable_based_on_taxable_salary": 1,
+ "round_to_the_nearest_integer": 1
+ })
if setup or test_tax:
make_salary_component(data, test_tax, company_list)
diff --git a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
index dccb5df..a0c3013 100644
--- a/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
+++ b/erpnext/payroll/doctype/salary_structure_assignment/salary_structure_assignment.py
@@ -43,7 +43,7 @@
def set_payroll_payable_account(self):
if not self.payroll_payable_account:
- payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payable_account')
+ payroll_payable_account = frappe.db.get_value('Company', self.company, 'default_payroll_payable_account')
if not payroll_payable_account:
payroll_payable_account = frappe.db.get_value(
"Account", {
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 3bc20f8..bed9c14 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -543,6 +543,7 @@
company: me.frm.doc.company,
order_type: me.frm.doc.order_type,
is_pos: cint(me.frm.doc.is_pos),
+ is_return: cint(me.frm.doc.is_return),
is_subcontracted: me.frm.doc.is_subcontracted,
transaction_date: me.frm.doc.transaction_date || me.frm.doc.posting_date,
ignore_pricing_rule: me.frm.doc.ignore_pricing_rule,
diff --git a/erpnext/regional/india/e_invoice/utils.py b/erpnext/regional/india/e_invoice/utils.py
index 102a2f0..abe1504 100644
--- a/erpnext/regional/india/e_invoice/utils.py
+++ b/erpnext/regional/india/e_invoice/utils.py
@@ -15,7 +15,7 @@
from pyqrcode import create as qrcreate
from frappe.integrations.utils import make_post_request, make_get_request
from erpnext.regional.india.utils import get_gst_accounts, get_place_of_supply
-from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date
+from frappe.utils.data import cstr, cint, format_date, flt, time_diff_in_seconds, now_datetime, add_to_date, get_link_to_form
def validate_einvoice_fields(doc):
einvoicing_enabled = cint(frappe.db.get_value('E Invoice Settings', 'E Invoice Settings', 'enable'))
@@ -84,29 +84,32 @@
))
def get_party_details(address_name):
- address = frappe.get_all('Address', filters={'name': address_name}, fields=['*'])[0]
- gstin = address.get('gstin')
+ d = frappe.get_all('Address', filters={'name': address_name}, fields=['*'])[0]
- gstin_details = get_gstin_details(gstin)
- legal_name = gstin_details.get('LegalName')
- location = gstin_details.get('AddrLoc') or address.get('city')
- state_code = gstin_details.get('StateCode')
- pincode = gstin_details.get('AddrPncd')
- address_line1 = '{} {}'.format(gstin_details.get('AddrBno'), gstin_details.get('AddrFlno'))
- address_line2 = '{} {}'.format(gstin_details.get('AddrBnm'), gstin_details.get('AddrSt'))
- email_id = address.get('email_id')
- phone = address.get('phone')
- # get last 10 digit
- phone = phone.replace(" ", "")[-10:] if phone else ''
+ if (not d.gstin
+ or not d.city
+ or not d.pincode
+ or not d.address_title
+ or not d.address_line1
+ or not d.gst_state_number):
- if state_code == 97:
+ frappe.throw(
+ msg=_('Address lines, city, pincode, gstin is mandatory for address {}. Please set them and try again.').format(
+ get_link_to_form('Address', address_name)
+ ),
+ title=_('Missing Address Fields')
+ )
+
+ if d.gst_state_number == 97:
# according to einvoice standard
pincode = 999999
return frappe._dict(dict(
- gstin=gstin, legal_name=legal_name, location=location,
- pincode=pincode, state_code=state_code, address_line1=address_line1,
- address_line2=address_line2, email=email_id, phone=phone
+ gstin=d.gstin, legal_name=d.address_title,
+ location=d.city, pincode=d.pincode,
+ state_code=d.gst_state_number,
+ address_line1=d.address_line1,
+ address_line2=d.address_line2
))
def get_gstin_details(gstin):
@@ -127,14 +130,22 @@
return GSPConnector.get_gstin_details(gstin)
def get_overseas_address_details(address_name):
- address_title, address_line1, address_line2, city, phone, email_id = frappe.db.get_value(
- 'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city', 'phone', 'email_id']
+ address_title, address_line1, address_line2, city = frappe.db.get_value(
+ 'Address', address_name, ['address_title', 'address_line1', 'address_line2', 'city']
)
+ if not address_title or not address_line1 or not city:
+ frappe.throw(
+ msg=_('Address lines and city is mandatory for address {}. Please set them and try again.').format(
+ get_link_to_form('Address', address_name)
+ ),
+ title=_('Missing Address Fields')
+ )
+
return frappe._dict(dict(
- gstin='URP', legal_name=address_title, address_line1=address_line1,
- address_line2=address_line2, email=email_id, phone=phone,
- pincode=999999, state_code=96, place_of_supply=96, location=city
+ gstin='URP', legal_name=address_title, location=city,
+ address_line1=address_line1, address_line2=address_line2,
+ pincode=999999, state_code=96, place_of_supply=96
))
def get_item_list(invoice):
@@ -146,9 +157,10 @@
item.update(d.as_dict())
item.sr_no = d.idx
- item.discount_amount = abs(item.discount_amount * item.qty)
- item.description = d.item_name
+ item.description = d.item_name.replace('"', '\\"')
+
item.qty = abs(item.qty)
+ item.discount_amount = abs(item.discount_amount * item.qty)
item.unit_rate = abs(item.base_amount / item.qty)
item.gross_amount = abs(item.base_amount)
item.taxable_value = abs(item.base_amount)
@@ -156,6 +168,7 @@
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.serial_no = ""
item = update_item_taxes(invoice, item)
@@ -272,7 +285,25 @@
vehicle_type=vehicle_type[invoice.gst_vehicle_type]
))
+def validate_mandatory_fields(invoice):
+ if not invoice.company_address:
+ frappe.throw(_('Company Address is mandatory to fetch company GSTIN details.'), title=_('Missing Fields'))
+ if not invoice.customer_address:
+ frappe.throw(_('Customer Address is mandatory to fetch customer GSTIN details.'), title=_('Missing Fields'))
+ if not frappe.db.get_value('Address', invoice.company_address, 'gstin'):
+ frappe.throw(
+ _('GSTIN is mandatory to fetch company GSTIN details. Please enter GSTIN in selected company address.'),
+ title=_('Missing Fields')
+ )
+ if not frappe.db.get_value('Address', invoice.customer_address, 'gstin'):
+ frappe.throw(
+ _('GSTIN is mandatory to fetch customer GSTIN details. Please enter GSTIN in selected customer address.'),
+ title=_('Missing Fields')
+ )
+
def make_einvoice(invoice):
+ validate_mandatory_fields(invoice)
+
schema = read_json('einv_template')
transaction_details = get_transaction_details(invoice)
@@ -351,7 +382,7 @@
# remove empty dicts
einvoice.pop(fieldname, None)
continue
-
+
# convert to int or str
if value_type == 'string':
einvoice[fieldname] = str(value)
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js
index 0e565a3..9089b53 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.js
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.js
@@ -74,67 +74,71 @@
return Object.assign(options, {
checkboxColumn: true,
events: {
- onCheckRow: function(data) {
+ onCheckRow: function (data) {
+ if (!data) return;
+ const data_doctype = $(
+ data[2].html
+ )[0].attributes.getNamedItem("data-doctype").value;
+ const tree_type = frappe.query_report.filters[0].value;
+ if (data_doctype != tree_type) return;
+
row_name = data[2].content;
length = data.length;
- var tree_type = frappe.query_report.filters[0].value;
-
- if(tree_type == "Customer") {
- row_values = data.slice(4,length-1).map(function (column) {
- return column.content;
- })
+ if (tree_type == "Customer") {
+ row_values = data
+ .slice(4, length - 1)
+ .map(function (column) {
+ return column.content;
+ });
} else if (tree_type == "Item") {
- row_values = data.slice(5,length-1).map(function (column) {
- return column.content;
- })
- }
- else {
- row_values = data.slice(3,length-1).map(function (column) {
- return column.content;
- })
+ row_values = data
+ .slice(5, length - 1)
+ .map(function (column) {
+ return column.content;
+ });
+ } else {
+ row_values = data
+ .slice(3, length - 1)
+ .map(function (column) {
+ return column.content;
+ });
}
entry = {
- 'name':row_name,
- 'values':row_values
- }
+ name: row_name,
+ values: row_values,
+ };
let raw_data = frappe.query_report.chart.data;
let new_datasets = raw_data.datasets;
- var found = false;
-
- for(var i=0; i < new_datasets.length;i++){
- if(new_datasets[i].name == row_name){
- found = true;
- new_datasets.splice(i,1);
- break;
+ let element_found = new_datasets.some((element, index, array)=>{
+ if(element.name == row_name){
+ array.splice(index, 1)
+ return true
}
- }
+ return false
+ })
- if(!found){
+ if (!element_found) {
new_datasets.push(entry);
}
let new_data = {
labels: raw_data.labels,
- datasets: new_datasets
- }
-
- setTimeout(() => {
- frappe.query_report.chart.update(new_data)
- }, 500)
-
-
- setTimeout(() => {
- frappe.query_report.chart.draw(true);
- }, 1000)
+ datasets: new_datasets,
+ };
+ chart_options = {
+ data: new_data,
+ type: "line",
+ };
+ frappe.query_report.render_chart(chart_options);
frappe.query_report.raw_chart_data = new_data;
},
- }
- })
+ },
+ });
},
}
diff --git a/erpnext/setup/doctype/company/delete_company_transactions.py b/erpnext/setup/doctype/company/delete_company_transactions.py
index 566f20c..7a72fe3 100644
--- a/erpnext/setup/doctype/company/delete_company_transactions.py
+++ b/erpnext/setup/doctype/company/delete_company_transactions.py
@@ -28,7 +28,7 @@
"Party Account", "Employee", "Sales Taxes and Charges Template",
"Purchase Taxes and Charges Template", "POS Profile", "BOM",
"Company", "Bank Account", "Item Tax Template", "Mode Of Payment",
- "Item Default"):
+ "Item Default", "Customer", "Supplier"):
delete_for_doctype(doctype, company_name)
# reset company values
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index 08f7a83..bf45251 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -74,7 +74,9 @@
update_party_blanket_order(args, out)
- get_price_list_rate(args, item, out)
+ if not doc or cint(doc.get('is_return')) == 0:
+ # get price list rate only if the invoice is not a credit or debit note
+ get_price_list_rate(args, item, out)
if args.customer and cint(args.is_pos):
out.update(get_pos_profile_item_details(args.company, args))