Merge pull request #23283 from ruchamahabal/fix-student-email-id
fix: Set student email id mandatory only if skip user creation is disabled
diff --git a/erpnext/accounts/desk_page/accounting/accounting.json b/erpnext/accounts/desk_page/accounting/accounting.json
index 2c52314..3f23ba9 100644
--- a/erpnext/accounts/desk_page/accounting/accounting.json
+++ b/erpnext/accounts/desk_page/accounting/accounting.json
@@ -43,7 +43,7 @@
{
"hidden": 0,
"label": "Bank Statement",
- "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Reconciliation\",\n \"name\": \"bank-reconciliation\",\n \"type\": \"page\"\n },\n {\n \"label\": \"Bank Clearance\",\n \"name\": \"Bank Clearance\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
+ "links": "[\n {\n \"label\": \"Bank\",\n \"name\": \"Bank\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Account\",\n \"name\": \"Bank Account\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Transaction Entry\",\n \"name\": \"Bank Statement Transaction Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"Bank Statement Settings\",\n \"name\": \"Bank Statement Settings\",\n \"type\": \"doctype\"\n }\n]"
},
{
"hidden": 0,
@@ -98,7 +98,7 @@
"idx": 0,
"is_standard": 1,
"label": "Accounting",
- "modified": "2020-09-03 10:37:07.865801",
+ "modified": "2020-09-09 11:45:33.766400",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
@@ -148,11 +148,6 @@
"type": "Report"
},
{
- "label": "Point of Sale",
- "link_to": "point-of-sale",
- "type": "Page"
- },
- {
"label": "Dashboard",
"link_to": "Accounts",
"type": "Dashboard"
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 8dcd2e4..9336fc3 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -55,14 +55,48 @@
},
callback: (r) => {
let pos_docs = r.message;
- set_form_data(pos_docs, frm)
- refresh_fields(frm)
- set_html_data(frm)
+ set_form_data(pos_docs, frm);
+ refresh_fields(frm);
+ set_html_data(frm);
}
})
}
});
+cur_frm.cscript.before_pos_transactions_remove = function(doc, cdt, cdn) {
+ const removed_row = locals[cdt][cdn];
+
+ if (!removed_row.pos_invoice) return;
+
+ frappe.db.get_doc("POS Invoice", removed_row.pos_invoice).then(doc => {
+ cur_frm.doc.grand_total -= flt(doc.grand_total);
+ cur_frm.doc.net_total -= flt(doc.net_total);
+ cur_frm.doc.total_quantity -= flt(doc.total_qty);
+ refresh_payments(doc, cur_frm, 1);
+ refresh_taxes(doc, cur_frm, 1);
+ refresh_fields(cur_frm);
+ set_html_data(cur_frm);
+ });
+}
+
+frappe.ui.form.on('POS Invoice Reference', {
+ pos_invoice(frm, cdt, cdn) {
+ const added_row = locals[cdt][cdn];
+
+ if (!added_row.pos_invoice) return;
+
+ frappe.db.get_doc("POS Invoice", added_row.pos_invoice).then(doc => {
+ frm.doc.grand_total += flt(doc.grand_total);
+ frm.doc.net_total += flt(doc.net_total);
+ frm.doc.total_quantity += flt(doc.total_qty);
+ refresh_payments(doc, frm);
+ refresh_taxes(doc, frm);
+ refresh_fields(frm);
+ set_html_data(frm);
+ });
+ }
+})
+
frappe.ui.form.on('POS Closing Entry Detail', {
closing_amount: (frm, cdt, cdn) => {
const row = locals[cdt][cdn];
@@ -76,8 +110,8 @@
frm.doc.grand_total += flt(d.grand_total);
frm.doc.net_total += flt(d.net_total);
frm.doc.total_quantity += flt(d.total_qty);
- add_to_payments(d, frm);
- add_to_taxes(d, frm);
+ refresh_payments(d, frm);
+ refresh_taxes(d, frm);
});
}
@@ -90,11 +124,12 @@
})
}
-function add_to_payments(d, frm) {
+function refresh_payments(d, frm, remove) {
d.payments.forEach(p => {
const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment);
if (payment) {
- payment.expected_amount += flt(p.amount);
+ if (!remove) payment.expected_amount += flt(p.amount);
+ else payment.expected_amount -= flt(p.amount);
} else {
frm.add_child("payment_reconciliation", {
mode_of_payment: p.mode_of_payment,
@@ -105,11 +140,12 @@
})
}
-function add_to_taxes(d, frm) {
+function refresh_taxes(d, frm, remove) {
d.taxes.forEach(t => {
const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate);
if (tax) {
- tax.amount += flt(t.tax_amount);
+ if (!remove) tax.amount += flt(t.tax_amount);
+ else tax.amount -= flt(t.tax_amount);
} else {
frm.add_child("taxes", {
account_head: t.account_head,
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index 2a2e3df..4780688 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -279,7 +279,8 @@
"fieldtype": "Check",
"label": "Is Return (Credit Note)",
"no_copy": 1,
- "print_hide": 1
+ "print_hide": 1,
+ "set_only_once": 1
},
{
"fieldname": "column_break1",
@@ -1578,9 +1579,10 @@
}
],
"icon": "fa fa-file-text",
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-05-29 15:08:39.337385",
+ "modified": "2020-09-07 12:43:09.138720",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 9c62a87..514a2ac 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -182,8 +182,9 @@
def test_pos_returns_with_repayment(self):
pos = create_pos_invoice(qty = 10, do_not_save=True)
+ pos.set('payments', [])
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 500})
- pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500})
+ pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 500, 'default': 1})
pos.insert()
pos.submit()
@@ -200,8 +201,9 @@
income_account = "Sales - _TC", expense_account = "Cost of Goods Sold - _TC", rate=105,
cost_center = "Main - _TC", do_not_save=True)
+ pos.set('payments', [])
pos.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 50})
- pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60})
+ pos.append("payments", {'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 60, 'default': 1})
pos.insert()
pos.submit()
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 00dbad5..11b9d25 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -24,11 +24,20 @@
def validate_pos_invoice_status(self):
for d in self.pos_invoices:
- status, docstatus = frappe.db.get_value('POS Invoice', d.pos_invoice, ['status', 'docstatus'])
+ status, docstatus, is_return, return_against = frappe.db.get_value(
+ 'POS Invoice', d.pos_invoice, ['status', 'docstatus', 'is_return', 'return_against'])
+
if docstatus != 1:
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, d.pos_invoice))
- if status in ['Consolidated']:
+ if status == "Consolidated":
frappe.throw(_("Row #{}: POS Invoice {} has been {}").format(d.idx, d.pos_invoice, status))
+ if is_return and return_against not in [d.pos_invoice for d in self.pos_invoices] and status != "Consolidated":
+ # if return entry is not getting merged in the current pos closing and if it is not consolidated
+ frappe.throw(
+ _("Row #{}: Return Invoice {} cannot be made against unconsolidated invoice. \
+ You can add original invoice {} manually to proceed.")
+ .format(d.idx, frappe.bold(d.pos_invoice), frappe.bold(return_against))
+ )
def on_submit(self):
pos_invoice_docs = [frappe.get_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
@@ -36,12 +45,12 @@
returns = [d for d in pos_invoice_docs if d.get('is_return') == 1]
sales = [d for d in pos_invoice_docs if d.get('is_return') == 0]
- sales_invoice = self.process_merging_into_sales_invoice(sales)
+ sales_invoice, credit_note = "", ""
+ if sales:
+ sales_invoice = self.process_merging_into_sales_invoice(sales)
- if len(returns):
+ if returns:
credit_note = self.process_merging_into_credit_note(returns)
- else:
- credit_note = ""
self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log
diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py
index a03dee1..afc5f81 100644
--- a/erpnext/controllers/sales_and_purchase_return.py
+++ b/erpnext/controllers/sales_and_purchase_return.py
@@ -242,7 +242,8 @@
'type': data.type,
'amount': -1 * paid_amount,
'base_amount': -1 * base_paid_amount,
- 'account': data.account
+ 'account': data.account,
+ 'default': data.default
})
if doc.is_pos:
doc.paid_amount = -1 * source.paid_amount
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js
index 8036c7d..f1634c1 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.js
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.js
@@ -34,10 +34,10 @@
if (frm.doc.docstatus === 1 && frm.doc.status !== 'Approved' && frm.doc.status !== 'Rejected') {
frm.add_custom_button(__('Approve'), function () {
status_update(1, frm);
- });
+ }, __('Actions'));
frm.add_custom_button(__('Reject'), function () {
status_update(0, frm);
- });
+ }, __('Actions'));
}
}
@@ -179,23 +179,6 @@
d.show();
};
-cur_frm.cscript.custom_before_submit = function (doc) {
- if (doc.normal_test_items) {
- for (let result in doc.normal_test_items) {
- if (!doc.normal_test_items[result].result_value && !doc.normal_test_items[result].allow_blank && doc.normal_test_items[result].require_result_value) {
- frappe.throw(__('Please input all required result values'));
- }
- }
- }
- if (doc.descriptive_test_items) {
- for (let result in doc.descriptive_test_items) {
- if (!doc.descriptive_test_items[result].result_value && !doc.descriptive_test_items[result].allow_blank && doc.descriptive_test_items[result].require_result_value) {
- frappe.throw(__('Please input all required result values'));
- }
- }
- }
-};
-
var make_dialog = function (frm, emailed, printed) {
var number = frm.doc.mobile;
@@ -203,7 +186,7 @@
title: 'Send SMS',
width: 400,
fields: [
- { fieldname: 'sms_type', fieldtype: 'Select', label: 'Type', options: ['Emailed', 'Printed'] },
+ { fieldname: 'result_format', fieldtype: 'Select', label: 'Result Format', options: ['Emailed', 'Printed'] },
{ fieldname: 'number', fieldtype: 'Data', label: 'Mobile Number', reqd: 1 },
{ fieldname: 'message', fieldtype: 'Small Text', label: 'Message', reqd: 1 }
],
@@ -217,22 +200,22 @@
dialog.hide();
}
});
- if (frm.doc.report_preference == 'Print') {
+ if (frm.doc.report_preference === 'Print') {
dialog.set_values({
- 'sms_type': 'Printed',
+ 'result_format': 'Printed',
'number': number,
'message': printed
});
} else {
dialog.set_values({
- 'sms_type': 'Emailed',
+ 'result_format': 'Emailed',
'number': number,
'message': emailed
});
}
var fd = dialog.fields_dict;
- $(fd.sms_type.input).change(function () {
- if (dialog.get_value('sms_type') == 'Emailed') {
+ $(fd.result_format.input).change(function () {
+ if (dialog.get_value('result_format') === 'Emailed') {
dialog.set_values({
'number': number,
'message': emailed
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json
index 2eb8014..edf1d91 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.json
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.json
@@ -84,7 +84,7 @@
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
- "options": "LP-",
+ "options": "HLC-LAB-.YYYY.-",
"print_hide": 1,
"report_hide": 1,
"reqd": 1
@@ -197,11 +197,10 @@
{
"fieldname": "status",
"fieldtype": "Select",
+ "in_list_view": 1,
"label": "Status",
"options": "Draft\nCompleted\nApproved\nRejected\nCancelled",
- "print_hide": 1,
"read_only": 1,
- "report_hide": 1,
"search_index": 1
},
{
@@ -249,8 +248,8 @@
{
"fieldname": "result_date",
"fieldtype": "Date",
- "hidden": 1,
"label": "Result Date",
+ "read_only": 1,
"search_index": 1
},
{
@@ -354,7 +353,8 @@
},
{
"fieldname": "sb_normal",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Compound Test Result"
},
{
"fieldname": "normal_test_items",
@@ -369,11 +369,13 @@
{
"depends_on": "descriptive_toggle",
"fieldname": "organisms_section",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Organism Test Result"
},
{
"fieldname": "sb_sensitivity",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Sensitivity Test Result"
},
{
"fieldname": "sensitivity_test_items",
@@ -383,8 +385,10 @@
"report_hide": 1
},
{
+ "collapsible": 1,
"fieldname": "sb_comments",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Comments"
},
{
"fieldname": "lab_test_comment",
@@ -531,7 +535,8 @@
},
{
"fieldname": "sb_descriptive",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Descriptive Test Result"
},
{
"default": "0",
@@ -550,7 +555,7 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2020-07-16 13:35:24.811062",
+ "modified": "2020-07-30 18:18:38.516215",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test",
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.py b/erpnext/healthcare/doctype/lab_test/lab_test.py
index 865f4a1..2db7743 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.py
@@ -6,23 +6,24 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.utils import getdate, cstr
+from frappe.utils import getdate, cstr, get_link_to_form
class LabTest(Document):
+ def validate(self):
+ if not self.is_new():
+ self.set_secondary_uom_result()
+
def on_submit(self):
+ self.validate_result_values()
self.db_set('submitted_date', getdate())
self.db_set('status', 'Completed')
insert_lab_test_to_medical_record(self)
def on_cancel(self):
- delete_lab_test_from_medical_record(self)
self.db_set('status', 'Cancelled')
+ delete_lab_test_from_medical_record(self)
self.reload()
- def validate(self):
- if not self.is_new():
- self.set_secondary_uom_result()
-
def on_update(self):
if self.sensitivity_test_items:
sensitivity = sorted(self.sensitivity_test_items, key=lambda x: x.antibiotic_sensitivity)
@@ -51,7 +52,20 @@
item.secondary_uom_result = float(item.result_value) * float(item.conversion_factor)
except:
item.secondary_uom_result = ''
- frappe.msgprint(_('Result for Secondary UOM not calculated for row #{0}'.format(item.idx)), title = _('Warning'))
+ frappe.msgprint(_('Row #{0}: Result for Secondary UOM not calculated'.format(item.idx)), title = _('Warning'))
+
+ def validate_result_values(self):
+ if self.normal_test_items:
+ for item in self.normal_test_items:
+ if not item.result_value and not item.allow_blank and item.require_result_value:
+ frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
+ item.idx, frappe.bold(item.lab_test_name)), title=_('Mandatory Results'))
+
+ if self.descriptive_test_items:
+ for item in self.descriptive_test_items:
+ if not item.result_value and not item.allow_blank and item.require_result_value:
+ frappe.throw(_('Row #{0}: Please enter the result value for {1}').format(
+ item.idx, frappe.bold(item.lab_test_particulars)), title=_('Mandatory Results'))
def create_test_from_template(lab_test):
@@ -89,7 +103,7 @@
lab_test_created = create_lab_test_from_encounter(docname)
if lab_test_created:
- frappe.msgprint(_('Lab Test(s) {0} created'.format(lab_test_created)))
+ frappe.msgprint(_('Lab Test(s) {0} created successfully').format(lab_test_created), indicator='green')
else:
frappe.msgprint(_('No Lab Tests created'))
@@ -211,8 +225,9 @@
'docstatus': 0,
'sample': template.sample
})
+
if sample_exists:
- # Update Sample Collection by adding quantity
+ # update sample collection by adding quantity
sample_collection = frappe.get_doc('Sample Collection', sample_exists[0][0])
quantity = int(sample_collection.sample_qty) + int(template.sample_qty)
if template.sample_details:
@@ -238,7 +253,7 @@
sample_collection.company = company
if template.sample_details:
- sample_collection.sample_details = 'Test :' + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
+ sample_collection.sample_details = _('Test :') + (template.get('lab_test_name') or template.get('template')) + '\n' + 'Collection Detials:\n\t' + template.sample_details
sample_collection.save(ignore_permissions=True)
return sample_collection
@@ -248,26 +263,31 @@
sample_collection = create_sample_doc(template, patient, invoice, lab_test.company)
if sample_collection:
lab_test.sample = sample_collection.name
-
+ sample_collection_doc = get_link_to_form('Sample Collection', sample_collection.name)
+ frappe.msgprint(_('Sample Collection {0} has been created').format(sample_collection_doc),
+ title=_('Sample Collection'), indicator='green')
return lab_test
def load_result_format(lab_test, template, prescription, invoice):
if template.lab_test_template_type == 'Single':
create_normals(template, lab_test)
+
elif template.lab_test_template_type == 'Compound':
create_compounds(template, lab_test, False)
+
elif template.lab_test_template_type == 'Descriptive':
create_descriptives(template, lab_test)
+
elif template.lab_test_template_type == 'Grouped':
# Iterate for each template in the group and create one result for all.
for lab_test_group in template.lab_test_groups:
# Template_in_group = None
if lab_test_group.lab_test_template:
- template_in_group = frappe.get_doc('Lab Test Template',
- lab_test_group.lab_test_template)
+ template_in_group = frappe.get_doc('Lab Test Template', lab_test_group.lab_test_template)
if template_in_group:
if template_in_group.lab_test_template_type == 'Single':
create_normals(template_in_group, lab_test)
+
elif template_in_group.lab_test_template_type == 'Compound':
normal_heading = lab_test.append('normal_test_items')
normal_heading.lab_test_name = template_in_group.lab_test_name
@@ -275,6 +295,7 @@
normal_heading.allow_blank = 1
normal_heading.template = template_in_group.name
create_compounds(template_in_group, lab_test, True)
+
elif template_in_group.lab_test_template_type == 'Descriptive':
descriptive_heading = lab_test.append('descriptive_test_items')
descriptive_heading.lab_test_name = template_in_group.lab_test_name
@@ -282,6 +303,7 @@
descriptive_heading.allow_blank = 1
descriptive_heading.template = template_in_group.name
create_descriptives(template_in_group, lab_test)
+
else: # Lab Test Group - Add New Line
normal = lab_test.append('normal_test_items')
normal.lab_test_name = lab_test_group.group_event
@@ -292,6 +314,7 @@
normal.allow_blank = lab_test_group.allow_blank
normal.require_result_value = 1
normal.template = template.name
+
if template.lab_test_template_type != 'No Result':
if prescription:
lab_test.prescription = prescription
@@ -302,9 +325,10 @@
@frappe.whitelist()
def get_employee_by_user_id(user_id):
- emp_id = frappe.db.get_value('Employee', { 'user_id': user_id })
- employee = frappe.get_doc('Employee', emp_id)
- return employee
+ emp_id = frappe.db.exists('Employee', { 'user_id': user_id })
+ if emp_id:
+ return frappe.get_doc('Employee', emp_id)
+ return None
def insert_lab_test_to_medical_record(doc):
table_row = False
@@ -325,7 +349,7 @@
table_row += ' ' + frappe.bold(_('Lab Test Result: ')) + item.result_value
if item.normal_range:
- table_row += ' ' + _('Normal Range:') + item.normal_range
+ table_row += ' ' + _('Normal Range: ') + item.normal_range
table_row += ' ' + comment
elif doc.descriptive_test_items:
@@ -356,7 +380,7 @@
medical_record.save(ignore_permissions = True)
def delete_lab_test_from_medical_record(self):
- medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name= %s', (self.name))
+ medical_record_id = frappe.db.sql('select name from `tabPatient Medical Record` where reference_name=%s', (self.name))
if medical_record_id and medical_record_id[0][0]:
frappe.delete_doc('Patient Medical Record', medical_record_id[0][0])
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test_list.js b/erpnext/healthcare/doctype/lab_test/lab_test_list.js
index b7f157c..7b5b9d9 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test_list.js
+++ b/erpnext/healthcare/doctype/lab_test/lab_test_list.js
@@ -3,13 +3,16 @@
*/
frappe.listview_settings['Lab Test'] = {
add_fields: ['name', 'status', 'invoiced'],
- filters: [['docstatus', '=', '0']],
+ filters: [['docstatus', '=', '1']],
get_indicator: function (doc) {
- if (doc.status == 'Approved') {
- return [__('Approved'), 'green', 'status, = ,Approved'];
- }
- if (doc.status == 'Rejected') {
+ if (doc.status === 'Approved') {
+ return [__('Approved'), 'green', 'status, =, Approved'];
+ } else if (doc.status === 'Rejected') {
return [__('Rejected'), 'orange', 'status, =, Rejected'];
+ } else if (doc.status === 'Completed') {
+ return [__('Completed'), 'green', 'status, =, Completed'];
+ } else if (doc.status === 'Cancelled') {
+ return [__('Cancelled'), 'red', 'status, =, Cancelled'];
}
},
onload: function (listview) {
@@ -21,7 +24,7 @@
var create_multiple_dialog = function (listview) {
var dialog = new frappe.ui.Dialog({
- title: 'Create Multiple Lab Test',
+ title: 'Create Multiple Lab Tests',
width: 100,
fields: [
{ fieldtype: 'Link', label: 'Patient', fieldname: 'patient', options: 'Patient', reqd: 1 },
@@ -41,7 +44,7 @@
}
}
],
- primary_action_label: __('Create Lab Test'),
+ primary_action_label: __('Create'),
primary_action: function () {
frappe.call({
method: 'erpnext.healthcare.doctype.lab_test.lab_test.create_multiple',
diff --git a/erpnext/healthcare/doctype/lab_test/test_lab_test.py b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
index 4131ad9..79ab8a4 100644
--- a/erpnext/healthcare/doctype/lab_test/test_lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/test_lab_test.py
@@ -3,8 +3,204 @@
# See license.txt
from __future__ import unicode_literals
import unittest
-
-# test_records = frappe.get_test_records('Lab Test')
+import frappe
+from frappe.utils import getdate, nowtime
+from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
+from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
+from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account
+from erpnext.healthcare.doctype.patient_medical_record.test_patient_medical_record import create_lab_test_template as create_blood_test_template
class TestLabTest(unittest.TestCase):
- pass
+ def test_lab_test_item(self):
+ lab_template = create_lab_test_template()
+ self.assertTrue(frappe.db.exists('Item', lab_template.item))
+ self.assertEqual(frappe.db.get_value('Item Price', {'item_code':lab_template.item}, 'price_list_rate'), lab_template.lab_test_rate)
+
+ lab_template.disabled = 1
+ lab_template.save()
+ self.assertEquals(frappe.db.get_value('Item', lab_template.item, 'disabled'), 1)
+
+ lab_template.reload()
+
+ lab_template.disabled = 0
+ lab_template.save()
+
+ def test_descriptive_lab_test(self):
+ lab_template = create_lab_test_template()
+
+ # blank result value not allowed as per template
+ lab_test = create_lab_test(lab_template)
+ lab_test.descriptive_test_items[0].result_value = 12
+ lab_test.descriptive_test_items[2].result_value = 1
+ lab_test.save()
+ self.assertRaises(frappe.ValidationError, lab_test.submit)
+
+ def test_sample_collection(self):
+ frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 1)
+ lab_template = create_lab_test_template()
+
+ lab_test = create_lab_test(lab_template)
+ lab_test.descriptive_test_items[0].result_value = 12
+ lab_test.descriptive_test_items[1].result_value = 1
+ lab_test.descriptive_test_items[2].result_value = 2.3
+ lab_test.save()
+
+ # check sample collection created
+ self.assertTrue(frappe.db.exists('Sample Collection', {'sample': lab_template.sample}))
+
+ frappe.db.set_value('Healthcare Settings', 'Healthcare Settings', 'create_sample_collection_for_lab_test', 0)
+ lab_test = create_lab_test(lab_template)
+ lab_test.descriptive_test_items[0].result_value = 12
+ lab_test.descriptive_test_items[1].result_value = 1
+ lab_test.descriptive_test_items[2].result_value = 2.3
+ lab_test.save()
+
+ # sample collection should not be created
+ lab_test.reload()
+ self.assertEquals(lab_test.sample, None)
+
+ def test_create_lab_tests_from_sales_invoice(self):
+ sales_invoice = create_sales_invoice()
+ create_multiple('Sales Invoice', sales_invoice.name)
+ sales_invoice.reload()
+ self.assertIsNotNone(sales_invoice.items[0].reference_dn)
+ self.assertIsNotNone(sales_invoice.items[1].reference_dn)
+
+ def test_create_lab_tests_from_patient_encounter(self):
+ patient_encounter = create_patient_encounter()
+ create_multiple('Patient Encounter', patient_encounter.name)
+ patient_encounter.reload()
+ self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
+ self.assertTrue(patient_encounter.lab_test_prescription[0].lab_test_created)
+
+
+def create_lab_test_template(test_sensitivity=0, sample_collection=1):
+ medical_department = create_medical_department()
+ if frappe.db.exists('Lab Test Template', 'Insulin Resistance'):
+ return frappe.get_doc('Lab Test Template', 'Insulin Resistance')
+ template = frappe.new_doc('Lab Test Template')
+ template.lab_test_name = 'Insulin Resistance'
+ template.lab_test_template_type = 'Descriptive'
+ template.lab_test_code = 'Insulin Resistance'
+ template.lab_test_group = 'Services'
+ template.department = medical_department
+ template.is_billable = 1
+ template.lab_test_description = 'Insulin Resistance'
+ template.lab_test_rate = 2000
+
+ for entry in ['FBS', 'Insulin', 'IR']:
+ template.append('descriptive_test_templates', {
+ 'particulars': entry,
+ 'allow_blank': 1 if entry=='IR' else 0
+ })
+
+ if test_sensitivity:
+ template.sensitivity = 1
+
+ if sample_collection:
+ template.sample = create_lab_test_sample()
+ template.sample_qty = 5.0
+
+ template.save()
+ return template
+
+def create_medical_department():
+ medical_department = frappe.db.exists('Medical Department', '_Test Medical Department')
+ if not medical_department:
+ medical_department = frappe.new_doc('Medical Department')
+ medical_department.department = '_Test Medical Department'
+ medical_department.save()
+ medical_department = medical_department.name
+
+ return medical_department
+
+def create_lab_test(lab_template):
+ patient = create_patient()
+ lab_test = frappe.new_doc('Lab Test')
+ lab_test.template = lab_template.name
+ lab_test.patient = patient
+ lab_test.patient_sex = 'Female'
+ lab_test.save()
+
+ return lab_test
+
+def create_lab_test_sample():
+ blood_sample = frappe.db.exists('Lab Test Sample', 'Blood Sample')
+ if blood_sample:
+ return blood_sample
+
+ sample = frappe.new_doc('Lab Test Sample')
+ sample.sample = 'Blood Sample'
+ sample.sample_uom = 'U/ml'
+ sample.save()
+
+ return sample.name
+
+def create_sales_invoice():
+ patient = create_patient()
+ medical_department = create_medical_department()
+ insulin_resistance_template = create_lab_test_template()
+ blood_test_template = create_blood_test_template(medical_department)
+
+ sales_invoice = frappe.new_doc('Sales Invoice')
+ sales_invoice.patient = patient
+ sales_invoice.customer = frappe.db.get_value('Patient', patient, 'customer')
+ sales_invoice.due_date = getdate()
+ sales_invoice.company = '_Test Company'
+ sales_invoice.debit_to = get_receivable_account('_Test Company')
+
+ tests = [insulin_resistance_template, blood_test_template]
+ for entry in tests:
+ sales_invoice.append('items', {
+ 'item_code': entry.item,
+ 'item_name': entry.lab_test_name,
+ 'description': entry.lab_test_description,
+ 'qty': 1,
+ 'uom': 'Nos',
+ 'conversion_factor': 1,
+ 'income_account': get_income_account(None, '_Test Company'),
+ 'rate': entry.lab_test_rate,
+ 'amount': entry.lab_test_rate
+ })
+
+ sales_invoice.set_missing_values()
+
+ sales_invoice.submit()
+ return sales_invoice
+
+def create_patient_encounter():
+ patient = create_patient()
+ medical_department = create_medical_department()
+ insulin_resistance_template = create_lab_test_template()
+ blood_test_template = create_blood_test_template(medical_department)
+
+ patient_encounter = frappe.new_doc('Patient Encounter')
+ patient_encounter.patient = patient
+ patient_encounter.practitioner = create_practitioner()
+ patient_encounter.encounter_date = getdate()
+ patient_encounter.encounter_time = nowtime()
+
+ tests = [insulin_resistance_template, blood_test_template]
+ for entry in tests:
+ patient_encounter.append('lab_test_prescription', {
+ 'lab_test_code': entry.item,
+ 'lab_test_name': entry.lab_test_name
+ })
+
+ patient_encounter.submit()
+ return patient_encounter
+
+
+def create_practitioner():
+ practitioner = frappe.db.exists('Healthcare Practitioner', '_Test Healthcare Practitioner')
+
+ if not practitioner:
+ practitioner = frappe.new_doc('Healthcare Practitioner')
+ practitioner.first_name = '_Test Healthcare Practitioner'
+ practitioner.gender = 'Female'
+ practitioner.op_consulting_charge = 500
+ practitioner.inpatient_visit_charge = 500
+ practitioner.save(ignore_permissions=True)
+ practitioner = practitioner.name
+
+ return practitioner
diff --git a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
index beea7a3..2767f7e 100644
--- a/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
+++ b/erpnext/healthcare/doctype/lab_test_group_template/lab_test_group_template.json
@@ -93,7 +93,8 @@
"depends_on": "secondary_uom",
"fieldname": "conversion_factor",
"fieldtype": "Float",
- "label": "Conversion Factor"
+ "label": "Conversion Factor",
+ "mandatory_depends_on": "secondary_uom"
},
{
"default": "0",
@@ -106,7 +107,7 @@
],
"istable": 1,
"links": [],
- "modified": "2020-06-24 10:59:01.921924",
+ "modified": "2020-07-30 12:36:03.082391",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Group Template",
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
index db64297..c3fc842 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.json
@@ -34,14 +34,15 @@
"descriptive_test_templates",
"section_break_group",
"lab_test_groups",
- "medical_coding_section",
- "medical_code_standard",
- "medical_code",
"sb_sample_collection",
"sample",
"sample_uom",
"sample_qty",
+ "column_break_33",
"sample_details",
+ "medical_coding_section",
+ "medical_code",
+ "medical_code_standard",
"worksheet_section",
"worksheet_instructions",
"result_legend_section",
@@ -112,7 +113,7 @@
{
"default": "1",
"depends_on": "eval:doc.lab_test_template_type != 'Grouped'",
- "description": "If unchecked, the item wont be appear in Sales Invoice, but can be used in group test creation. ",
+ "description": "If unchecked, the item will not be available in Sales Invoices for billing but can be used in group test creation. ",
"fieldname": "is_billable",
"fieldtype": "Check",
"label": "Is Billable",
@@ -128,6 +129,7 @@
"mandatory_depends_on": "eval:doc.is_billable == 1"
},
{
+ "collapsible": 1,
"fieldname": "medical_coding_section",
"fieldtype": "Section Break",
"label": "Medical Coding"
@@ -184,7 +186,7 @@
"depends_on": "eval:doc.lab_test_template_type == 'Descriptive'",
"fieldname": "section_break_special",
"fieldtype": "Section Break",
- "label": "Descriptive"
+ "label": "Descriptive Test"
},
{
"default": "0",
@@ -196,7 +198,7 @@
"depends_on": "eval:doc.lab_test_template_type == 'Grouped'",
"fieldname": "section_break_group",
"fieldtype": "Section Break",
- "label": "Group"
+ "label": "Group Tests"
},
{
"fieldname": "lab_test_groups",
@@ -217,7 +219,6 @@
"no_copy": 1
},
{
- "collapsible": 1,
"fieldname": "sb_sample_collection",
"fieldtype": "Section Break",
"label": "Sample Collection"
@@ -311,10 +312,14 @@
"fieldname": "descriptive_test_templates",
"fieldtype": "Table",
"options": "Descriptive Test Template"
+ },
+ {
+ "fieldname": "column_break_33",
+ "fieldtype": "Column Break"
}
],
"links": [],
- "modified": "2020-07-13 12:57:09.925436",
+ "modified": "2020-07-30 14:32:40.449818",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Lab Test Template",
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
index 6f0d08c..543dee2 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template.py
@@ -15,7 +15,8 @@
def validate(self):
if self.is_billable and (not self.lab_test_rate or self.lab_test_rate <= 0.0):
- frappe.throw(_("Standard Selling Rate should be greater than zero."))
+ frappe.throw(_('Standard Selling Rate should be greater than zero.'))
+
self.validate_conversion_factor()
self.enable_disable_item()
@@ -42,7 +43,9 @@
# Remove template reference from item and disable item
if self.item:
try:
- frappe.delete_doc('Item', self.item)
+ item = self.item
+ self.db_set('item', '')
+ frappe.delete_doc('Item', item)
except Exception:
frappe.throw(_('Not permitted. Please disable the Lab Test Template'))
@@ -63,26 +66,26 @@
'standard_rate': self.lab_test_rate,
'description': self.lab_test_description
})
- item.save()
+ item.flags.ignore_mandatory = True
+ item.save(ignore_permissions=True)
def item_price_exists(self):
item_price = frappe.db.exists({'doctype': 'Item Price', 'item_code': self.lab_test_code})
if item_price:
return item_price[0][0]
- else:
- return False
+ return False
def validate_conversion_factor(self):
- if self.lab_test_template_type == "Single" and self.secondary_uom and not self.conversion_factor:
- frappe.throw(_("Conversion Factor is mandatory"))
- if self.lab_test_template_type == "Compound":
+ if self.lab_test_template_type == 'Single' and self.secondary_uom and not self.conversion_factor:
+ frappe.throw(_('Conversion Factor is mandatory'))
+ if self.lab_test_template_type == 'Compound':
for item in self.normal_test_templates:
if item.secondary_uom and not item.conversion_factor:
- frappe.throw(_("Conversion Factor is mandatory"))
- if self.lab_test_template_type == "Grouped":
+ frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(item.idx))
+ if self.lab_test_template_type == 'Grouped':
for group in self.lab_test_groups:
- if group.template_or_new_line == "Add New Line" and group.secondary_uom and not group.conversion_factor:
- frappe.throw(_("Conversion Factor is mandatory"))
+ if group.template_or_new_line == 'Add New Line' and group.secondary_uom and not group.conversion_factor:
+ frappe.throw(_('Row #{0}: Conversion Factor is mandatory').format(group.idx))
def create_item_from_template(doc):
@@ -101,9 +104,9 @@
'include_item_in_manufacturing': 0,
'show_in_website': 0,
'is_pro_applicable': 0,
- 'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled,
+ 'disabled': 0 if doc.is_billable and not doc.disabled else doc.disabled,
'stock_uom': uom
- }).insert(ignore_permissions = True, ignore_mandatory = True)
+ }).insert(ignore_permissions=True, ignore_mandatory=True)
# Insert item price
if doc.is_billable and doc.lab_test_rate != 0.0:
@@ -123,7 +126,7 @@
'price_list': price_list_name,
'item_code': item,
'price_list_rate': item_price
- }).insert(ignore_permissions = True, ignore_mandatory = True)
+ }).insert(ignore_permissions=True, ignore_mandatory=True)
@frappe.whitelist()
def change_test_code_from_template(lab_test_code, doc):
@@ -132,8 +135,8 @@
if frappe.db.exists({'doctype': 'Item', 'item_code': lab_test_code}):
frappe.throw(_('Lab Test Item {0} already exist').format(lab_test_code))
else:
- rename_doc('Item', doc.name, lab_test_code, ignore_permissions = True)
+ rename_doc('Item', doc.name, lab_test_code, ignore_permissions=True)
frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_code', lab_test_code)
frappe.db.set_value('Lab Test Template', doc.name, 'lab_test_name', lab_test_code)
- rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions = True)
+ rename_doc('Lab Test Template', doc.name, lab_test_code, ignore_permissions=True)
return lab_test_code
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py
new file mode 100644
index 0000000..94dfeea
--- /dev/null
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_dashboard.py
@@ -0,0 +1,13 @@
+from __future__ import unicode_literals
+from frappe import _
+
+def get_data():
+ return {
+ 'fieldname': 'template',
+ 'transactions': [
+ {
+ 'label': _('Lab Tests'),
+ 'items': ['Lab Test']
+ }
+ ]
+ }
diff --git a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
index a3417eb..08fc2cd 100644
--- a/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
+++ b/erpnext/healthcare/doctype/lab_test_template/lab_test_template_list.js
@@ -3,5 +3,5 @@
*/
frappe.listview_settings['Lab Test Template'] = {
add_fields: ['lab_test_name', 'lab_test_code', 'lab_test_rate'],
- filters: [['disabled', '=', 0]]
+ filters: [['disabled', '=', 'No']]
};
diff --git a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
index e5a5e4c..aa85a23 100644
--- a/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
+++ b/erpnext/healthcare/doctype/patient_medical_record/test_patient_medical_record.py
@@ -34,7 +34,7 @@
self.assertTrue(medical_rec)
template = create_lab_test_template(medical_department)
- lab_test = create_lab_test(template, patient)
+ lab_test = create_lab_test(template.name, patient)
# check for lab test
medical_rec = frappe.db.exists('Patient Medical Record', {'status': 'Open', 'reference_name': lab_test.name})
self.assertTrue(medical_rec)
@@ -66,7 +66,7 @@
def create_lab_test_template(medical_department):
if frappe.db.exists('Lab Test Template', 'Blood Test'):
- return 'Blood Test'
+ return frappe.get_doc('Lab Test Template', 'Blood Test')
template = frappe.new_doc('Lab Test Template')
template.lab_test_name = 'Blood Test'
@@ -76,7 +76,7 @@
template.is_billable = 1
template.lab_test_rate = 2000
template.save()
- return template.name
+ return template
def create_lab_test(template, patient):
lab_test = frappe.new_doc('Lab Test')
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.js b/erpnext/healthcare/doctype/sample_collection/sample_collection.js
index 2f5278b..0390391 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.js
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.js
@@ -3,29 +3,29 @@
frappe.ui.form.on('Sample Collection', {
refresh: function(frm) {
- if(frappe.defaults.get_default("create_sample_collection_for_lab_test")){
- frm.add_custom_button(__("View Lab Tests"), function() {
- frappe.route_options = {"sample": frm.doc.name};
- frappe.set_route("List", "Lab Test");
+ if (frappe.defaults.get_default('create_sample_collection_for_lab_test')) {
+ frm.add_custom_button(__('View Lab Tests'), function() {
+ frappe.route_options = {'sample': frm.doc.name};
+ frappe.set_route('List', 'Lab Test');
});
}
}
});
-frappe.ui.form.on("Sample Collection", "patient", function(frm) {
+frappe.ui.form.on('Sample Collection', 'patient', function(frm) {
if(frm.doc.patient){
frappe.call({
- "method": "erpnext.healthcare.doctype.patient.patient.get_patient_detail",
+ 'method': 'erpnext.healthcare.doctype.patient.patient.get_patient_detail',
args: {
patient: frm.doc.patient
},
callback: function (data) {
var age = null;
- if(data.message.dob){
+ if (data.message.dob){
age = calculate_age(data.message.dob);
}
- frappe.model.set_value(frm.doctype,frm.docname, "patient_age", age);
- frappe.model.set_value(frm.doctype,frm.docname, "patient_sex", data.message.sex);
+ frappe.model.set_value(frm.doctype,frm.docname, 'patient_age', age);
+ frappe.model.set_value(frm.doctype,frm.docname, 'patient_sex', data.message.sex);
}
});
}
@@ -36,5 +36,5 @@
var age = new Date();
age.setTime(ageMS);
var years = age.getFullYear() - 1970;
- return years + " Year(s) " + age.getMonth() + " Month(s) " + age.getDate() + " Day(s)";
+ return years + ' Year(s) ' + age.getMonth() + ' Month(s) ' + age.getDate() + ' Day(s)';
};
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
index 016cfbc..83383e3 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
@@ -9,8 +9,10 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
+ "patient_details_section",
"naming_series",
"patient",
+ "patient_name",
"patient_age",
"patient_sex",
"column_break_4",
@@ -25,15 +27,17 @@
"collected_by",
"collected_time",
"num_print",
- "amended_from",
"section_break_15",
- "sample_details"
+ "sample_details",
+ "amended_from"
],
"fields": [
{
"fetch_from": "patient.inpatient_record",
"fieldname": "inpatient_record",
"fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
"label": "Inpatient Record",
"options": "Inpatient Record",
"read_only": 1
@@ -42,6 +46,8 @@
"bold": 1,
"fieldname": "naming_series",
"fieldtype": "Select",
+ "hide_days": 1,
+ "hide_seconds": 1,
"label": "Series",
"no_copy": 1,
"options": "HLC-SC-.YYYY.-",
@@ -52,6 +58,8 @@
"default": "0",
"fieldname": "invoiced",
"fieldtype": "Check",
+ "hide_days": 1,
+ "hide_seconds": 1,
"label": "Invoiced",
"no_copy": 1,
"read_only": 1,
@@ -61,41 +69,60 @@
"fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
"ignore_user_permissions": 1,
"in_standard_filter": 1,
"label": "Patient",
"options": "Patient",
+ "reqd": 1,
"search_index": 1
},
{
"fieldname": "column_break_4",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "hide_days": 1,
+ "hide_seconds": 1
},
{
"fieldname": "patient_age",
"fieldtype": "Data",
- "label": "Age"
+ "hide_days": 1,
+ "hide_seconds": 1,
+ "label": "Age",
+ "read_only": 1
},
{
"fetch_from": "patient.sex",
"fieldname": "patient_sex",
- "fieldtype": "Data",
- "label": "Gender"
+ "fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
+ "label": "Gender",
+ "options": "Gender",
+ "read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
"in_standard_filter": 1,
"label": "Company",
"options": "Company"
},
{
"fieldname": "section_break_6",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "hide_days": 1,
+ "hide_seconds": 1,
+ "label": "Sample Details"
},
{
"fieldname": "sample",
"fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
"ignore_user_permissions": 1,
"in_list_view": 1,
"in_standard_filter": 1,
@@ -108,16 +135,23 @@
"fetch_from": "sample.sample_uom",
"fieldname": "sample_uom",
"fieldtype": "Data",
+ "hide_days": 1,
+ "hide_seconds": 1,
"in_list_view": 1,
- "label": "UOM"
+ "label": "UOM",
+ "read_only": 1
},
{
"fieldname": "column_break_10",
- "fieldtype": "Column Break"
+ "fieldtype": "Column Break",
+ "hide_days": 1,
+ "hide_seconds": 1
},
{
"fieldname": "collected_by",
"fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
"ignore_user_permissions": 1,
"label": "Collected By",
"options": "User"
@@ -125,20 +159,27 @@
{
"fieldname": "collected_time",
"fieldtype": "Datetime",
- "label": "Collected Time"
+ "hide_days": 1,
+ "hide_seconds": 1,
+ "label": "Collected On"
},
{
"allow_on_submit": 1,
"default": "1",
+ "description": "Number of prints required for labelling the samples",
"fieldname": "num_print",
"fieldtype": "Int",
- "label": "No. of print",
+ "hide_days": 1,
+ "hide_seconds": 1,
+ "label": "No. of prints",
"print_hide": 1,
"report_hide": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
+ "hide_days": 1,
+ "hide_seconds": 1,
"label": "Amended From",
"no_copy": 1,
"options": "Sample Collection",
@@ -147,25 +188,43 @@
},
{
"fieldname": "section_break_15",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "hide_days": 1,
+ "hide_seconds": 1
},
{
"default": "0",
"fieldname": "sample_qty",
"fieldtype": "Float",
+ "hide_days": 1,
+ "hide_seconds": 1,
"in_list_view": 1,
"label": "Quantity"
},
{
"fieldname": "sample_details",
"fieldtype": "Long Text",
+ "hide_days": 1,
+ "hide_seconds": 1,
"ignore_xss_filter": 1,
"label": "Collection Details"
+ },
+ {
+ "fieldname": "patient_details_section",
+ "fieldtype": "Section Break",
+ "label": "Patient Details"
+ },
+ {
+ "fetch_from": "patient.patient_name",
+ "fieldname": "patient_name",
+ "fieldtype": "Data",
+ "label": "Patient Name",
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-05-25 14:36:46.990469",
+ "modified": "2020-07-30 16:53:13.076104",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Sample Collection",
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.py b/erpnext/healthcare/doctype/sample_collection/sample_collection.py
index 2c64320..461f809 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.py
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.py
@@ -3,7 +3,12 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+import frappe
from frappe.model.document import Document
+from frappe.utils import flt
+from frappe import _
class SampleCollection(Document):
- pass
+ def validate(self):
+ if flt(self.sample_qty) <= 0:
+ frappe.throw(_('Sample Quantity cannot be negative or 0'), title=_('Invalid Quantity'))
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.js b/erpnext/healthcare/report/lab_test_report/lab_test_report.js
index 3128f81..7754e2e 100644
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.js
+++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.js
@@ -4,29 +4,54 @@
frappe.query_reports["Lab Test Report"] = {
"filters": [
{
- "fieldname":"from_date",
+ "fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
- "default": frappe.datetime.now_date(),
- "width": "80"
+ "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
+ "reqd": 1
},
{
- "fieldname":"to_date",
+ "fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
- "default": frappe.datetime.now_date()
+ "default": frappe.datetime.now_date(),
+ "reqd": 1
},
{
- "fieldname":"patient",
+ "fieldname": "company",
+ "label": __("Company"),
+ "fieldtype": "Link",
+ "default": frappe.defaults.get_default("Company"),
+ "options": "Company"
+ },
+ {
+ "fieldname": "template",
+ "label": __("Lab Test Template"),
+ "fieldtype": "Link",
+ "options": "Lab Test Template"
+ },
+ {
+ "fieldname": "patient",
"label": __("Patient"),
"fieldtype": "Link",
"options": "Patient"
},
{
- "fieldname":"department",
+ "fieldname": "department",
"label": __("Medical Department"),
"fieldtype": "Link",
"options": "Medical Department"
+ },
+ {
+ "fieldname": "status",
+ "label": __("Status"),
+ "fieldtype": "Select",
+ "options": "\nCompleted\nApproved\nRejected"
+ },
+ {
+ "fieldname": "invoiced",
+ "label": __("Invoiced"),
+ "fieldtype": "Check"
}
]
};
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.json b/erpnext/healthcare/report/lab_test_report/lab_test_report.json
index 30e5a5f..aeb4289 100644
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.json
+++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.json
@@ -1,30 +1,31 @@
{
- "add_total_row": 1,
- "creation": "2013-04-23 18:15:29",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 1,
- "is_standard": "Yes",
- "modified": "2018-08-06 11:41:50.218737",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Lab Test Report",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Lab Test",
- "report_name": "Lab Test Report",
- "report_type": "Script Report",
+ "add_total_row": 0,
+ "creation": "2013-04-23 18:15:29",
+ "disable_prepared_report": 0,
+ "disabled": 0,
+ "docstatus": 0,
+ "doctype": "Report",
+ "idx": 1,
+ "is_standard": "Yes",
+ "modified": "2020-07-30 18:53:20.102873",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Lab Test Report",
+ "owner": "Administrator",
+ "prepared_report": 0,
+ "ref_doctype": "Lab Test",
+ "report_name": "Lab Test Report",
+ "report_type": "Script Report",
"roles": [
{
"role": "Laboratory User"
- },
+ },
{
"role": "Nursing User"
- },
+ },
{
"role": "LabTest Approver"
- },
+ },
{
"role": "Healthcare Administrator"
}
diff --git a/erpnext/healthcare/report/lab_test_report/lab_test_report.py b/erpnext/healthcare/report/lab_test_report/lab_test_report.py
index 17f25fa..2e59bed 100644
--- a/erpnext/healthcare/report/lab_test_report/lab_test_report.py
+++ b/erpnext/healthcare/report/lab_test_report/lab_test_report.py
@@ -8,51 +8,204 @@
def execute(filters=None):
if not filters: filters = {}
- lab_test_list = get_lab_test(filters)
+ data, columns = [], []
+
columns = get_columns()
+ lab_test_list = get_lab_tests(filters)
if not lab_test_list:
- msgprint(_("No record found"))
+ msgprint(_('No records found'))
return columns, lab_test_list
data = []
for lab_test in lab_test_list:
- row = [ lab_test.lab_test_name, lab_test.patient, lab_test.practitioner, lab_test.invoiced, lab_test.status, lab_test.result_date, lab_test.department]
+ row = frappe._dict({
+ 'test': lab_test.name,
+ 'template': lab_test.template,
+ 'company': lab_test.company,
+ 'patient': lab_test.patient,
+ 'patient_name': lab_test.patient_name,
+ 'practitioner': lab_test.practitioner,
+ 'employee': lab_test.employee,
+ 'status': lab_test.status,
+ 'invoiced': lab_test.invoiced,
+ 'result_date': lab_test.result_date,
+ 'department': lab_test.department
+ })
data.append(row)
- return columns, data
+ chart = get_chart_data(data)
+ report_summary = get_report_summary(data)
+ return columns, data, None, chart, report_summary
def get_columns():
- columns = [
- _("Test") + ":Data:120",
- _("Patient") + ":Link/Patient:180",
- _("Healthcare Practitioner") + ":Link/Healthcare Practitioner:120",
- _("Invoiced") + ":Check:100",
- _("Status") + ":Data:120",
- _("Result Date") + ":Date:120",
- _("Department") + ":Data:120",
+ return [
+ {
+ 'fieldname': 'test',
+ 'label': _('Lab Test'),
+ 'fieldtype': 'Link',
+ 'options': 'Lab Test',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'template',
+ 'label': _('Lab Test Template'),
+ 'fieldtype': 'Link',
+ 'options': 'Lab Test Template',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'company',
+ 'label': _('Company'),
+ 'fieldtype': 'Link',
+ 'options': 'Company',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'patient',
+ 'label': _('Patient'),
+ 'fieldtype': 'Link',
+ 'options': 'Patient',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'patient_name',
+ 'label': _('Patient Name'),
+ 'fieldtype': 'Data',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'employee',
+ 'label': _('Lab Technician'),
+ 'fieldtype': 'Link',
+ 'options': 'Employee',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'status',
+ 'label': _('Status'),
+ 'fieldtype': 'Data',
+ 'width': '100'
+ },
+ {
+ 'fieldname': 'invoiced',
+ 'label': _('Invoiced'),
+ 'fieldtype': 'Check',
+ 'width': '100'
+ },
+ {
+ 'fieldname': 'result_date',
+ 'label': _('Result Date'),
+ 'fieldtype': 'Date',
+ 'width': '100'
+ },
+ {
+ 'fieldname': 'practitioner',
+ 'label': _('Requesting Practitioner'),
+ 'fieldtype': 'Link',
+ 'options': 'Healthcare Practitioner',
+ 'width': '120'
+ },
+ {
+ 'fieldname': 'department',
+ 'label': _('Medical Department'),
+ 'fieldtype': 'Link',
+ 'options': 'Medical Department',
+ 'width': '100'
+ }
]
- return columns
+def get_lab_tests(filters):
+ conditions = get_conditions(filters)
+ data = frappe.get_all(
+ doctype='Lab Test',
+ fields=['name', 'template', 'company', 'patient', 'patient_name', 'practitioner', 'employee', 'status', 'invoiced', 'result_date', 'department'],
+ filters=conditions,
+ order_by='submitted_date desc'
+ )
+ return data
def get_conditions(filters):
- conditions = ""
+ conditions = {
+ 'docstatus': ('=', 1)
+ }
- if filters.get("patient"):
- conditions += "and patient = %(patient)s"
- if filters.get("from_date"):
- conditions += "and result_date >= %(from_date)s"
- if filters.get("to_date"):
- conditions += " and result_date <= %(to_date)s"
- if filters.get("department"):
- conditions += " and department = %(department)s"
+ if filters.get('from_date') and filters.get('to_date'):
+ conditions['result_date'] = ('between', (filters.get('from_date'), filters.get('to_date')))
+ filters.pop('from_date')
+ filters.pop('to_date')
+
+ for key, value in filters.items():
+ if filters.get(key):
+ conditions[key] = value
return conditions
-def get_lab_test(filters):
- conditions = get_conditions(filters)
- return frappe.db.sql("""select name, patient, lab_test_name, patient_name, status, result_date, practitioner, invoiced, department
- from `tabLab Test`
- where docstatus<2 %s order by submitted_date desc, name desc""" %
- conditions, filters, as_dict=1)
+def get_chart_data(data):
+ if not data:
+ return None
+
+ labels = ['Completed', 'Approved', 'Rejected']
+
+ status_wise_data = {
+ 'Completed': 0,
+ 'Approved': 0,
+ 'Rejected': 0
+ }
+
+ datasets = []
+
+ for entry in data:
+ status_wise_data[entry.status] += 1
+
+ datasets.append({
+ 'name': 'Lab Test Status',
+ 'values': [status_wise_data.get('Completed'), status_wise_data.get('Approved'), status_wise_data.get('Rejected')]
+ })
+
+ chart = {
+ 'data': {
+ 'labels': labels,
+ 'datasets': datasets
+ },
+ 'type': 'donut',
+ 'height': 300,
+ }
+
+ return chart
+
+
+def get_report_summary(data):
+ if not data:
+ return None
+
+ total_lab_tests = len(data)
+ invoiced_lab_tests, unbilled_lab_tests = 0, 0
+
+ for entry in data:
+ if entry.invoiced:
+ invoiced_lab_tests += 1
+ else:
+ unbilled_lab_tests += 1
+
+ return [
+ {
+ 'value': total_lab_tests,
+ 'indicator': 'Blue',
+ 'label': 'Total Lab Tests',
+ 'datatype': 'Int',
+ },
+ {
+ 'value': invoiced_lab_tests,
+ 'indicator': 'Green',
+ 'label': 'Invoiced Lab Tests',
+ 'datatype': 'Int',
+ },
+ {
+ 'value': unbilled_lab_tests,
+ 'indicator': 'Red',
+ 'label': 'Unbilled Lab Tests',
+ 'datatype': 'Int',
+ }
+ ]
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 6c58f2f..9ce570e 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -725,3 +725,4 @@
erpnext.patches.v13_0.drop_razorpay_payload_column
erpnext.patches.v13_0.update_start_end_date_for_old_shift_assignment
erpnext.patches.v13_0.setting_custom_roles_for_some_regional_reports
+erpnext.patches.v13_0.change_default_pos_print_format
diff --git a/erpnext/patches/v13_0/change_default_pos_print_format.py b/erpnext/patches/v13_0/change_default_pos_print_format.py
new file mode 100644
index 0000000..605a29e
--- /dev/null
+++ b/erpnext/patches/v13_0/change_default_pos_print_format.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.db.sql(
+ """UPDATE `tabPOS Profile` profile
+ SET profile.`print_format` = 'POS Invoice'
+ WHERE profile.`print_format` = 'Point of Sale'""")
\ No newline at end of file
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 6951539..6d58fd2 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -673,23 +673,14 @@
);
}
- frappe.db.get_value('Sales Invoice Payment', {'parent': this.frm.doc.pos_profile, 'default': 1},
- ['mode_of_payment', 'account', 'type'], (value) => {
- if (this.frm.is_dirty()) {
- frappe.model.clear_table(this.frm.doc, 'payments');
- if (value) {
- let row = frappe.model.add_child(this.frm.doc, 'Sales Invoice Payment', 'payments');
- row.mode_of_payment = value.mode_of_payment;
- row.type = value.type;
- row.account = value.account;
- row.default = 1;
- row.amount = total_amount_to_pay;
- } else {
- this.frm.set_value('is_pos', 1);
- }
- this.frm.refresh_fields();
- }
- }, 'Sales Invoice');
+ this.frm.doc.payments.find(pay => {
+ if (pay.default) {
+ pay.amount = total_amount_to_pay;
+ } else {
+ pay.amount = 0.0
+ }
+ });
+ this.frm.refresh_fields();
this.calculate_paid_amount();
},
diff --git a/erpnext/selling/desk_page/retail/retail.json b/erpnext/selling/desk_page/retail/retail.json
index 581e14c..c4ddf26 100644
--- a/erpnext/selling/desk_page/retail/retail.json
+++ b/erpnext/selling/desk_page/retail/retail.json
@@ -2,8 +2,18 @@
"cards": [
{
"hidden": 0,
- "label": "Retail Operations",
- "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point of Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"dependencies\": [\n \"POS Profile\"\n ],\n \"description\": \"Point of Sale\",\n \"label\": \"Point of Sale\",\n \"name\": \"point-of-sale\",\n \"onboard\": 1,\n \"type\": \"page\"\n },\n {\n \"description\": \"Setup mode of POS (Online / Offline)\",\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Cashier Closing\",\n \"label\": \"Cashier Closing\",\n \"name\": \"Cashier Closing\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
+ "label": "Settings & Configurations",
+ "links": "[\n {\n \"description\": \"Setup default values for POS Invoices\",\n \"label\": \"Point-of-Sale Profile\",\n \"name\": \"POS Profile\",\n \"onboard\": 1,\n \"type\": \"doctype\"\n },\n {\n \"label\": \"POS Settings\",\n \"name\": \"POS Settings\",\n \"type\": \"doctype\"\n }\n]"
+ },
+ {
+ "hidden": 0,
+ "label": "Loyalty Program",
+ "links": "[\n {\n \"description\": \"To make Customer based incentive schemes.\",\n \"label\": \"Loyalty Program\",\n \"name\": \"Loyalty Program\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"To view logs of Loyalty Points assigned to a Customer.\",\n \"label\": \"Loyalty Point Entry\",\n \"name\": \"Loyalty Point Entry\",\n \"type\": \"doctype\"\n }\n]"
+ },
+ {
+ "hidden": 0,
+ "label": "Opening & Closing",
+ "links": "[\n {\n \"label\": \"POS Opening Entry\",\n \"name\": \"POS Opening Entry\",\n \"type\": \"doctype\"\n },\n {\n \"label\": \"POS Closing Entry\",\n \"name\": \"POS Closing Entry\",\n \"type\": \"doctype\"\n }\n]"
}
],
"category": "Domains",
@@ -18,7 +28,7 @@
"idx": 0,
"is_standard": 1,
"label": "Retail",
- "modified": "2020-08-20 18:00:07.515691",
+ "modified": "2020-09-09 11:46:28.297435",
"modified_by": "Administrator",
"module": "Selling",
"name": "Retail",
@@ -28,25 +38,10 @@
"restrict_to_domain": "Retail",
"shortcuts": [
{
- "color": "#9deca2",
"doc_view": "",
- "format": "{} Active",
- "label": "Point of Sale Profile",
- "link_to": "POS Profile",
- "stats_filter": "{\n \"disabled\": 0\n}",
- "type": "DocType"
- },
- {
- "doc_view": "",
- "label": "Point of Sale",
+ "label": "Point Of Sale",
"link_to": "point-of-sale",
"type": "Page"
- },
- {
- "doc_view": "",
- "label": "POS Settings",
- "link_to": "POS Settings",
- "type": "DocType"
}
]
}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js
index 2ce0b27..8d4ac78 100644
--- a/erpnext/selling/page/point_of_sale/point_of_sale.js
+++ b/erpnext/selling/page/point_of_sale/point_of_sale.js
@@ -9,7 +9,7 @@
title: __('Point of Sale'),
single_column: true
});
- // online
+
wrapper.pos = new erpnext.PointOfSale.Controller(wrapper);
window.cur_pos = wrapper.pos;
};
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index ae5471b..5018254 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -8,7 +8,7 @@
{% include "erpnext/selling/page/point_of_sale/pos_past_order_summary.js" %}
erpnext.PointOfSale.Controller = class {
- constructor(wrapper) {
+ constructor(wrapper) {
this.wrapper = $(wrapper).find('.layout-main-section');
this.page = wrapper.page;
@@ -36,7 +36,7 @@
const table_fields = [
{ fieldname: "mode_of_payment", fieldtype: "Link", in_list_view: 1, label: "Mode of Payment", options: "Mode of Payment", reqd: 1 },
{ fieldname: "opening_amount", fieldtype: "Currency", default: 0, in_list_view: 1, label: "Opening Amount",
- options: "company:company_currency", reqd: 1 }
+ options: "company:company_currency" }
];
const dialog = new frappe.ui.Dialog({
@@ -51,29 +51,16 @@
options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
onchange: () => {
const pos_profile = dialog.fields_dict.pos_profile.get_value();
- const company = dialog.fields_dict.company.get_value();
- const user = frappe.session.user
- if (!pos_profile || !company || !user) return;
+ if (!pos_profile) return;
- // auto fetch last closing entry's balance details
- frappe.db.get_list("POS Closing Entry", {
- filters: { company, pos_profile, user },
- limit: 1,
- order_by: 'period_end_date desc'
- }).then((res) => {
- if (!res.length) return;
- const pos_closing_entry = res[0];
- frappe.db.get_doc("POS Closing Entry", pos_closing_entry.name).then(({ payment_reconciliation }) => {
- dialog.fields_dict.balance_details.df.data = [];
- payment_reconciliation.forEach(pay => {
- const { mode_of_payment } = pay;
- dialog.fields_dict.balance_details.df.data.push({
- mode_of_payment: mode_of_payment
- });
- });
- dialog.fields_dict.balance_details.grid.refresh();
+ frappe.db.get_doc("POS Profile", pos_profile).then(doc => {
+ dialog.fields_dict.balance_details.df.data = [];
+ doc.payments.forEach(pay => {
+ const { mode_of_payment } = pay;
+ dialog.fields_dict.balance_details.df.data.push({ mode_of_payment });
});
+ dialog.fields_dict.balance_details.grid.refresh();
});
}
},
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index eadeb8f..724b60b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -1,36 +1,36 @@
erpnext.PointOfSale.ItemCart = class {
- constructor({ wrapper, events }) {
+ constructor({ wrapper, events }) {
this.wrapper = wrapper;
this.events = events;
- this.customer_info = undefined;
-
- this.init_component();
- }
-
- init_component() {
- this.prepare_dom();
- this.init_child_components();
+ this.customer_info = undefined;
+
+ this.init_component();
+ }
+
+ init_component() {
+ this.prepare_dom();
+ this.init_child_components();
this.bind_events();
this.attach_shortcuts();
- }
+ }
- prepare_dom() {
+ prepare_dom() {
this.wrapper.append(
- `<section class="col-span-4 flex flex-col shadow rounded item-cart bg-white mx-h-70 h-100"></section>`
- )
+ `<section class="col-span-4 flex flex-col shadow rounded item-cart bg-white mx-h-70 h-100"></section>`
+ )
- this.$component = this.wrapper.find('.item-cart');
- }
+ this.$component = this.wrapper.find('.item-cart');
+ }
- init_child_components() {
- this.init_customer_selector();
- this.init_cart_components();
- }
+ init_child_components() {
+ this.init_customer_selector();
+ this.init_cart_components();
+ }
- init_customer_selector() {
+ init_customer_selector() {
this.$component.append(
- `<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
- )
+ `<div class="customer-section rounded flex flex-col m-8 mb-0"></div>`
+ )
this.$customer_section = this.$component.find('.customer-section');
}
@@ -41,9 +41,9 @@
this.make_customer_selector();
this.customer_field.set_focus();
}
-
- init_cart_components() {
- this.$component.append(
+
+ init_cart_components() {
+ this.$component.append(
`<div class="cart-container flex flex-col items-center rounded flex-1 relative">
<div class="absolute flex flex-col p-8 pt-0 w-full h-full">
<div class="flex text-grey cart-header pt-2 pb-2 p-4 mt-2 mb-2 w-full f-shrink-0">
@@ -55,23 +55,23 @@
<div class="cart-totals-section flex flex-col w-full mt-4 f-shrink-0"></div>
<div class="numpad-section flex flex-col mt-4 d-none w-full p-8 pt-0 pb-0 f-shrink-0"></div>
</div>
- </div>`
- );
+ </div>`
+ );
this.$cart_container = this.$component.find('.cart-container');
this.make_cart_totals_section();
this.make_cart_items_section();
- this.make_cart_numpad();
- }
+ this.make_cart_numpad();
+ }
- make_cart_items_section() {
- this.$cart_header = this.$component.find('.cart-header');
- this.$cart_items_wrapper = this.$component.find('.cart-items-section');
+ make_cart_items_section() {
+ this.$cart_header = this.$component.find('.cart-header');
+ this.$cart_items_wrapper = this.$component.find('.cart-items-section');
this.make_no_items_placeholder();
- }
-
- make_no_items_placeholder() {
+ }
+
+ make_no_items_placeholder() {
this.$cart_header.addClass('d-none');
this.$cart_items_wrapper.html(
`<div class="no-item-wrapper flex items-center h-18">
@@ -81,8 +81,8 @@
this.$cart_items_wrapper.addClass('mt-4 border-grey border-dashed');
}
- make_cart_totals_section() {
- this.$totals_section = this.$component.find('.cart-totals-section');
+ make_cart_totals_section() {
+ this.$totals_section = this.$component.find('.cart-totals-section');
this.$totals_section.append(
`<div class="add-discount flex items-center pt-4 pb-4 pr-4 pl-4 text-grey pointer no-select d-none">
@@ -116,9 +116,9 @@
)
this.$add_discount_elem = this.$component.find(".add-discount");
- }
-
- make_cart_numpad() {
+ }
+
+ make_cart_numpad() {
this.$numpad_section = this.$component.find('.numpad-section');
this.number_pad = new erpnext.PointOfSale.NumberPad({
@@ -155,9 +155,9 @@
Checkout
</div>`
)
- }
-
- bind_events() {
+ }
+
+ bind_events() {
const me = this;
this.$customer_section.on('click', '.add-remove-customer', function (e) {
const customer_info_is_visible = me.$cart_container.hasClass('d-none');
@@ -381,8 +381,8 @@
`
);
}
-
- update_customer_section() {
+
+ update_customer_section() {
const { customer, email_id='', mobile_no='', image } = this.customer_info || {};
if (customer) {
@@ -403,7 +403,7 @@
</div>`
);
} else {
- // reset customer selector
+ // reset customer selector
this.reset_customer_selector();
}
@@ -430,9 +430,9 @@
</div>`
}
}
- }
-
- update_totals_section(frm) {
+ }
+
+ update_totals_section(frm) {
if (!frm) frm = this.events.get_frm();
this.render_net_total(frm.doc.base_net_total);
@@ -440,9 +440,9 @@
const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
- }
-
- render_net_total(value) {
+ }
+
+ render_net_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.net-total').html(
`<div class="flex flex-col">
@@ -454,9 +454,9 @@
)
this.$numpad_section.find('.numpad-net-total').html(`Net Total: <span class="text-bold">${format_currency(value, currency)}</span>`)
- }
-
- render_grand_total(value) {
+ }
+
+ render_grand_total(value) {
const currency = this.events.get_frm().doc.currency;
this.$totals_section.find('.grand-total').html(
`<div class="flex flex-col">
@@ -495,20 +495,20 @@
} else {
this.$totals_section.find('.taxes').html('')
}
- }
+ }
- get_cart_item({ item_code, batch_no, uom }) {
+ get_cart_item({ item_code, batch_no, uom }) {
const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
const uom_attr = `[data-uom=${escape(uom)}]`;
- const item_selector = batch_no ?
- `.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
-
- return this.$cart_items_wrapper.find(item_selector);
- }
-
- update_item_html(item, remove_item) {
+ const item_selector = batch_no ?
+ `.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
+
+ return this.$cart_items_wrapper.find(item_selector);
+ }
+
+ update_item_html(item, remove_item) {
const $item = this.get_cart_item(item);
if (remove_item) {
@@ -524,33 +524,33 @@
const no_of_cart_items = this.$cart_items_wrapper.children().length;
no_of_cart_items > 0 && this.highlight_checkout_btn(no_of_cart_items > 0);
-
+
this.update_empty_cart_section(no_of_cart_items);
}
-
- render_cart_item(item_data, $item_to_update) {
+
+ render_cart_item(item_data, $item_to_update) {
const currency = this.events.get_frm().doc.currency;
const me = this;
- if (!$item_to_update.length) {
- this.$cart_items_wrapper.append(
- `<div class="cart-item-wrapper flex items-center h-18 pr-4 pl-4 rounded border-grey pointer no-select"
+ if (!$item_to_update.length) {
+ this.$cart_items_wrapper.append(
+ `<div class="cart-item-wrapper flex items-center h-18 pr-4 pl-4 rounded border-grey pointer no-select"
data-item-code="${escape(item_data.item_code)}" data-uom="${escape(item_data.uom)}"
data-batch-no="${escape(item_data.batch_no || '')}">
- </div>`
- )
- $item_to_update = this.get_cart_item(item_data);
- }
+ </div>`
+ )
+ $item_to_update = this.get_cart_item(item_data);
+ }
$item_to_update.html(
`<div class="flex flex-col flex-1 f-shrink-1 overflow-hidden whitespace-nowrap">
- <div class="text-md text-dark-grey text-bold">
- ${item_data.item_name}
- </div>
- ${get_description_html()}
- </div>
- ${get_rate_discount_html()}
- </div>`
+ <div class="text-md text-dark-grey text-bold">
+ ${item_data.item_name}
+ </div>
+ ${get_description_html()}
+ </div>
+ ${get_rate_discount_html()}
+ </div>`
)
set_dynamic_rate_header_width();
@@ -572,7 +572,7 @@
me.$cart_header.find(".rate-list-header").css("width", max_width);
me.$cart_items_wrapper.find(".rate-col").css("width", max_width);
}
-
+
function get_rate_discount_html() {
if (item_data.rate && item_data.amount && item_data.rate !== item_data.amount) {
return `
@@ -625,7 +625,7 @@
$item_to_update.attr(`data-${selector}`, value);
}
- toggle_checkout_btn(show_checkout) {
+ toggle_checkout_btn(show_checkout) {
if (show_checkout) {
this.$totals_section.find('.checkout-btn').removeClass('d-none');
this.$totals_section.find('.edit-cart-btn').addClass('d-none');
@@ -635,7 +635,7 @@
}
}
- highlight_checkout_btn(toggle) {
+ highlight_checkout_btn(toggle) {
const has_primary_class = this.$totals_section.find('.checkout-btn').hasClass('bg-primary');
if (toggle && !has_primary_class) {
this.$totals_section.find('.checkout-btn').addClass('bg-primary text-white text-lg');
@@ -643,8 +643,8 @@
this.$totals_section.find('.checkout-btn').removeClass('bg-primary text-white text-lg');
}
}
-
- update_empty_cart_section(no_of_cart_items) {
+
+ update_empty_cart_section(no_of_cart_items) {
const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper');
// if cart has items and no item is present
@@ -652,27 +652,27 @@
&& this.$cart_items_wrapper.removeClass('mt-4 border-grey border-dashed') && this.$cart_header.removeClass('d-none');
no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder();
- }
-
- on_numpad_event($btn) {
+ }
+
+ on_numpad_event($btn) {
const current_action = $btn.attr('data-button-value');
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
this.highlight_numpad_btn($btn, current_action);
- const action_is_pressed_twice = this.prev_action === current_action;
- const first_click_event = !this.prev_action;
- const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
+ const action_is_pressed_twice = this.prev_action === current_action;
+ const first_click_event = !this.prev_action;
+ const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
if (action_is_field_edit) {
if (first_click_event || field_to_edit_changed) {
- this.prev_action = current_action;
+ this.prev_action = current_action;
} else if (action_is_pressed_twice) {
this.prev_action = undefined;
}
- this.numpad_value = '';
-
+ this.numpad_value = '';
+
} else if (current_action === 'checkout') {
this.prev_action = undefined;
this.toggle_item_highlight();
@@ -688,7 +688,7 @@
this.numpad_value = this.numpad_value || 0;
}
- const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
+ const first_click_event_is_not_field_edit = !action_is_field_edit && first_click_event;
if (first_click_event_is_not_field_edit) {
frappe.show_alert({
@@ -708,34 +708,34 @@
this.numpad_value = current_action;
}
- this.events.numpad_event(this.numpad_value, this.prev_action);
- }
-
- highlight_numpad_btn($btn, curr_action) {
- const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
- const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
+ this.events.numpad_event(this.numpad_value, this.prev_action);
+ }
+
+ highlight_numpad_btn($btn, curr_action) {
+ const curr_action_is_highlighted = $btn.hasClass('shadow-inner');
+ const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action);
- if (!curr_action_is_highlighted) {
- $btn.addClass('shadow-inner bg-selected');
- }
- if (this.prev_action === curr_action && curr_action_is_highlighted) {
- // if Qty is pressed twice
- $btn.removeClass('shadow-inner bg-selected');
- }
- if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
- // Order: Qty -> Rate then remove Qty highlight
- const prev_btn = $(`[data-button-value='${this.prev_action}']`);
- prev_btn.removeClass('shadow-inner bg-selected');
- }
- if (!curr_action_is_action || curr_action === 'done') {
- // if numbers are clicked
- setTimeout(() => {
- $btn.removeClass('shadow-inner bg-selected');
- }, 100);
- }
- }
+ if (!curr_action_is_highlighted) {
+ $btn.addClass('shadow-inner bg-selected');
+ }
+ if (this.prev_action === curr_action && curr_action_is_highlighted) {
+ // if Qty is pressed twice
+ $btn.removeClass('shadow-inner bg-selected');
+ }
+ if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) {
+ // Order: Qty -> Rate then remove Qty highlight
+ const prev_btn = $(`[data-button-value='${this.prev_action}']`);
+ prev_btn.removeClass('shadow-inner bg-selected');
+ }
+ if (!curr_action_is_action || curr_action === 'done') {
+ // if numbers are clicked
+ setTimeout(() => {
+ $btn.removeClass('shadow-inner bg-selected');
+ }, 100);
+ }
+ }
- toggle_numpad(show) {
+ toggle_numpad(show) {
if (show) {
this.$totals_section.addClass('d-none');
this.$numpad_section.removeClass('d-none');
@@ -946,6 +946,6 @@
toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
- }
-
+ }
+
}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index 86a1be9..3a5f89b 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -1,28 +1,28 @@
erpnext.PointOfSale.ItemDetails = class {
- constructor({ wrapper, events }) {
+ constructor({ wrapper, events }) {
this.wrapper = wrapper;
- this.events = events;
- this.current_item = {};
+ this.events = events;
+ this.current_item = {};
- this.init_component();
- }
+ this.init_component();
+ }
- init_component() {
- this.prepare_dom();
- this.init_child_components();
+ init_component() {
+ this.prepare_dom();
+ this.init_child_components();
this.bind_events();
this.attach_shortcuts();
- }
+ }
- prepare_dom() {
- this.wrapper.append(
- `<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>`
- )
+ prepare_dom() {
+ this.wrapper.append(
+ `<section class="col-span-4 flex shadow rounded item-details bg-white mx-h-70 h-100 d-none"></section>`
+ )
- this.$component = this.wrapper.find('.item-details');
- }
+ this.$component = this.wrapper.find('.item-details');
+ }
- init_child_components() {
+ init_child_components() {
this.$component.html(
`<div class="details-container flex flex-col p-8 rounded w-full">
<div class="flex justify-between mb-2">
@@ -49,28 +49,28 @@
this.$item_image = this.$component.find('.item-image');
this.$form_container = this.$component.find('.form-container');
this.$dicount_section = this.$component.find('.discount-section');
- }
+ }
- toggle_item_details_section(item) {
+ toggle_item_details_section(item) {
const { item_code, batch_no, uom } = this.current_item;
const item_code_is_same = item && item_code === item.item_code;
const batch_is_same = item && batch_no == item.batch_no;
const uom_is_same = item && uom === item.uom;
- this.item_has_changed = !item ? false : item_code_is_same && batch_is_same && uom_is_same ? false : true;
+ this.item_has_changed = !item ? false : item_code_is_same && batch_is_same && uom_is_same ? false : true;
- this.events.toggle_item_selector(this.item_has_changed);
+ this.events.toggle_item_selector(this.item_has_changed);
this.toggle_component(this.item_has_changed);
-
+
if (this.item_has_changed) {
- this.doctype = item.doctype;
+ this.doctype = item.doctype;
this.item_meta = frappe.get_meta(this.doctype);
this.name = item.name;
this.item_row = item;
- this.currency = this.events.get_frm().doc.currency;
-
- this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom };
-
+ this.currency = this.events.get_frm().doc.currency;
+
+ this.current_item = { item_code: item.item_code, batch_no: item.batch_no, uom: item.uom };
+
this.render_dom(item);
this.render_discount_dom(item);
this.render_form(item);
@@ -102,9 +102,9 @@
this.events.remove_item_from_cart();
}
}
-
- render_dom(item) {
- let { item_code ,item_name, description, image, price_list_rate } = item;
+
+ render_dom(item) {
+ let { item_code ,item_name, description, image, price_list_rate } = item;
function get_description_html() {
if (description) {
@@ -112,8 +112,8 @@
return description;
}
return ``;
- }
-
+ }
+
this.$item_name.html(item_name);
this.$item_description.html(get_description_html());
this.$item_price.html(format_currency(price_list_rate, this.currency));
@@ -125,9 +125,9 @@
this.$item_image.html(frappe.get_abbr(item_code));
}
- }
-
- render_discount_dom(item) {
+ }
+
+ render_discount_dom(item) {
if (item.discount_percentage) {
this.$dicount_section.html(
`<div class="text-grey line-through mr-4 text-md mb-2">
@@ -141,9 +141,9 @@
} else {
this.$dicount_section.html(``)
}
- }
+ }
- render_form(item) {
+ render_form(item) {
const fields_to_display = this.get_form_fields(item);
this.$form_container.html('');
@@ -157,7 +157,7 @@
const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname);
fieldname === 'discount_percentage' ? (field_meta.label = __('Discount (%)')) : '';
const me = this;
-
+
this[`${fieldname}_control`] = frappe.ui.form.make_control({
df: {
...field_meta,
@@ -174,16 +174,16 @@
this.make_auto_serial_selection_btn(item);
this.bind_custom_control_change_event();
- }
+ }
- get_form_fields(item) {
+ get_form_fields(item) {
const fields = ['qty', 'uom', 'rate', 'price_list_rate', 'discount_percentage', 'warehouse', 'actual_qty'];
if (item.has_serial_no) fields.push('serial_no');
if (item.has_batch_no) fields.push('batch_no');
return fields;
}
- make_auto_serial_selection_btn(item) {
+ make_auto_serial_selection_btn(item) {
if (item.has_serial_no) {
this.$form_container.append(
`<div class="grid-filler no-select"></div>`
@@ -203,8 +203,8 @@
this.$form_container.find('.serial_no-control').parent().addClass('row-span-2');
}
}
-
- bind_custom_control_change_event() {
+
+ bind_custom_control_change_event() {
const me = this;
if (this.rate_control) {
this.rate_control.df.onchange = function() {
@@ -276,8 +276,8 @@
};
this.batch_no_control.df.onchange = function() {
me.events.set_value_in_current_cart_item('batch-no', this.value);
- me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
- me.current_item.batch_no = this.value;
+ me.events.form_updated(me.doctype, me.name, 'batch_no', this.value);
+ me.current_item.batch_no = this.value;
}
this.batch_no_control.refresh();
}
@@ -289,9 +289,9 @@
me.current_item.uom = this.value;
}
}
- }
-
- async auto_update_batch_no() {
+ }
+
+ async auto_update_batch_no() {
if (this.serial_no_control && this.batch_no_control) {
const selected_serial_nos = this.serial_no_control.get_value().split(`\n`).filter(s => s);
if (!selected_serial_nos.length) return;
@@ -310,9 +310,9 @@
const batch_no = Object.keys(batch_serial_map)[0];
const batch_serial_nos = batch_serial_map[batch_no].join(`\n`);
// eg. 10 selected serial no. -> 5 belongs to first batch other 5 belongs to second batch
- const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length;
-
- const current_batch_no = this.batch_no_control.get_value();
+ const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length;
+
+ const current_batch_no = this.batch_no_control.get_value();
current_batch_no != batch_no && await this.batch_no_control.set_value(batch_no);
if (serial_nos_belongs_to_other_batch) {
@@ -326,8 +326,8 @@
this.events.clone_new_batch_item_in_frm(batch_serial_map, this.current_item);
}
}
-
- bind_events() {
+
+ bind_events() {
this.bind_auto_serial_fetch_event();
this.bind_fields_to_numpad_fields();
@@ -345,7 +345,7 @@
});
}
- bind_fields_to_numpad_fields() {
+ bind_fields_to_numpad_fields() {
const me = this;
this.$form_container.on('click', '.input-with-feedback', function() {
const fieldname = $(this).attr('data-fieldname');
@@ -355,8 +355,8 @@
}
});
}
-
- bind_auto_serial_fetch_event() {
+
+ bind_auto_serial_fetch_event() {
this.$form_container.on('click', '.auto-fetch-btn', () => {
this.batch_no_control.set_value('');
let qty = this.qty_control.get_value();
@@ -382,7 +382,7 @@
frappe.msgprint(`Fetched only ${records_length} available serial numbers.`);
this.qty_control.set_value(records_length);
}
- numbers = auto_fetched_serial_numbers.join(`\n`);
+ numbers = auto_fetched_serial_numbers.join(`\n`);
this.serial_no_control.set_value(numbers);
});
})
@@ -390,5 +390,5 @@
toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
- }
+ }
}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_item_selector.js b/erpnext/selling/page/point_of_sale/pos_item_selector.js
index ee0c06d..c87b845 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_selector.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js
@@ -1,115 +1,115 @@
erpnext.PointOfSale.ItemSelector = class {
- constructor({ frm, wrapper, events, pos_profile }) {
+ constructor({ frm, wrapper, events, pos_profile }) {
this.wrapper = wrapper;
this.events = events;
- this.pos_profile = pos_profile;
-
- this.inti_component();
- }
-
- inti_component() {
- this.prepare_dom();
- this.make_search_bar();
- this.load_items_data();
- this.bind_events();
- this.attach_shortcuts();
- }
+ this.pos_profile = pos_profile;
+
+ this.inti_component();
+ }
+
+ inti_component() {
+ this.prepare_dom();
+ this.make_search_bar();
+ this.load_items_data();
+ this.bind_events();
+ this.attach_shortcuts();
+ }
- prepare_dom() {
+ prepare_dom() {
this.wrapper.append(
- `<section class="col-span-6 flex shadow rounded items-selector bg-white mx-h-70 h-100">
- <div class="flex flex-col rounded w-full scroll-y">
- <div class="filter-section flex p-8 pb-2 bg-white sticky z-100">
- <div class="search-field flex f-grow-3 mr-8 items-center text-grey"></div>
- <div class="item-group-field flex f-grow-1 items-center text-grey text-bold"></div>
- </div>
- <div class="flex flex-1 flex-col p-8 pt-2">
- <div class="text-grey mb-6">ALL ITEMS</div>
- <div class="items-container grid grid-cols-4 gap-8">
- </div>
- </div>
- </div>
- </section>`
- );
-
- this.$component = this.wrapper.find('.items-selector');
- }
+ `<section class="col-span-6 flex shadow rounded items-selector bg-white mx-h-70 h-100">
+ <div class="flex flex-col rounded w-full scroll-y">
+ <div class="filter-section flex p-8 pb-2 bg-white sticky z-100">
+ <div class="search-field flex f-grow-3 mr-8 items-center text-grey"></div>
+ <div class="item-group-field flex f-grow-1 items-center text-grey text-bold"></div>
+ </div>
+ <div class="flex flex-1 flex-col p-8 pt-2">
+ <div class="text-grey mb-6">ALL ITEMS</div>
+ <div class="items-container grid grid-cols-4 gap-8">
+ </div>
+ </div>
+ </div>
+ </section>`
+ );
+
+ this.$component = this.wrapper.find('.items-selector');
+ }
- async load_items_data() {
- if (!this.item_group) {
- const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name");
- this.parent_item_group = res.message.name;
- };
- if (!this.price_list) {
- const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
- this.price_list = res.message.selling_price_list;
- }
+ async load_items_data() {
+ if (!this.item_group) {
+ const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name");
+ this.parent_item_group = res.message.name;
+ };
+ if (!this.price_list) {
+ const res = await frappe.db.get_value("POS Profile", this.pos_profile, "selling_price_list");
+ this.price_list = res.message.selling_price_list;
+ }
- this.get_items({}).then(({message}) => {
- this.render_item_list(message.items);
- });
- }
+ this.get_items({}).then(({message}) => {
+ this.render_item_list(message.items);
+ });
+ }
- get_items({start = 0, page_length = 40, search_value=''}) {
- const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
- let { item_group, pos_profile } = this;
+ get_items({start = 0, page_length = 40, search_value=''}) {
+ const price_list = this.events.get_frm().doc?.selling_price_list || this.price_list;
+ let { item_group, pos_profile } = this;
- !item_group && (item_group = this.parent_item_group);
-
+ !item_group && (item_group = this.parent_item_group);
+
return frappe.call({
method: "erpnext.selling.page.point_of_sale.point_of_sale.get_items",
freeze: true,
- args: { start, page_length, price_list, item_group, search_value, pos_profile },
- });
+ args: { start, page_length, price_list, item_group, search_value, pos_profile },
+ });
}
render_item_list(items) {
- this.$items_container = this.$component.find('.items-container');
- this.$items_container.html('');
+ this.$items_container = this.$component.find('.items-container');
+ this.$items_container.html('');
- items.forEach(item => {
- const item_html = this.get_item_html(item);
- this.$items_container.append(item_html);
- })
- }
+ items.forEach(item => {
+ const item_html = this.get_item_html(item);
+ this.$items_container.append(item_html);
+ })
+ }
- get_item_html(item) {
- const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
- const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
+ get_item_html(item) {
+ const { item_image, serial_no, batch_no, barcode, actual_qty, stock_uom } = item;
+ const indicator_color = actual_qty > 10 ? "green" : actual_qty !== 0 ? "orange" : "red";
- function get_item_image_html() {
- if (item_image) {
- return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
- <img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;">
- </div>`
- } else {
- return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
- ${frappe.get_abbr(item.item_name)}
- </div>`
- }
- }
+ function get_item_image_html() {
+ if (item_image) {
+ return `<div class="flex items-center justify-center h-32 border-b-grey text-6xl text-grey-100">
+ <img class="h-full" src="${item_image}" alt="${item_image}" style="object-fit: cover;">
+ </div>`
+ } else {
+ return `<div class="flex items-center justify-center h-32 bg-light-grey text-6xl text-grey-100">
+ ${frappe.get_abbr(item.item_name)}
+ </div>`
+ }
+ }
return (
- `<div class="item-wrapper rounded shadow pointer no-select" data-item-code="${escape(item.item_code)}"
- data-serial-no="${escape(serial_no)}" data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
- title="Avaiable Qty: ${actual_qty}">
- ${get_item_image_html()}
- <div class="flex items-center pr-4 pl-4 h-10 justify-between">
- <div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
- <span class="indicator ${indicator_color}"></span>
- ${frappe.ellipsis(item.item_name, 18)}
- </div>
- <div class="f-shrink-0 text-dark-grey text-bold ml-4">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
- </div>
- </div>`
- )
- }
+ `<div class="item-wrapper rounded shadow pointer no-select" data-item-code="${escape(item.item_code)}"
+ data-serial-no="${escape(serial_no)}" data-batch-no="${escape(batch_no)}" data-uom="${escape(stock_uom)}"
+ title="Avaiable Qty: ${actual_qty}">
+ ${get_item_image_html()}
+ <div class="flex items-center pr-4 pl-4 h-10 justify-between">
+ <div class="flex items-center f-shrink-1 text-dark-grey overflow-hidden whitespace-nowrap">
+ <span class="indicator ${indicator_color}"></span>
+ ${frappe.ellipsis(item.item_name, 18)}
+ </div>
+ <div class="f-shrink-0 text-dark-grey text-bold ml-4">${format_currency(item.price_list_rate, item.currency, 0) || 0}</div>
+ </div>
+ </div>`
+ )
+ }
- make_search_bar() {
- const me = this;
- this.$component.find('.search-field').html('');
- this.$component.find('.item-group-field').html('');
+ make_search_bar() {
+ const me = this;
+ this.$component.find('.search-field').html('');
+ this.$component.find('.item-group-field').html('');
this.search_field = frappe.ui.form.make_control({
df: {
@@ -119,104 +119,104 @@
},
parent: this.$component.find('.search-field'),
render_input: true,
- });
+ });
this.item_group_field = frappe.ui.form.make_control({
df: {
label: __('Item Group'),
fieldtype: 'Link',
options: 'Item Group',
- placeholder: __('Select item group'),
- onchange: function() {
- me.item_group = this.value;
- !me.item_group && (me.item_group = me.parent_item_group);
- me.filter_items();
- },
- get_query: function () {
- return {
- query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
- filters: {
- pos_profile: me.events.get_frm().doc?.pos_profile
- }
- }
- },
+ placeholder: __('Select item group'),
+ onchange: function() {
+ me.item_group = this.value;
+ !me.item_group && (me.item_group = me.parent_item_group);
+ me.filter_items();
+ },
+ get_query: function () {
+ return {
+ query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query',
+ filters: {
+ pos_profile: me.events.get_frm().doc?.pos_profile
+ }
+ }
+ },
},
- parent: this.$component.find('.item-group-field'),
+ parent: this.$component.find('.item-group-field'),
render_input: true,
- });
- this.search_field.toggle_label(false);
+ });
+ this.search_field.toggle_label(false);
this.item_group_field.toggle_label(false);
}
- bind_events() {
- const me = this;
- onScan.attachTo(document, {
- onScan: (sScancode) => {
- if (this.search_field && this.$component.is(':visible')) {
- this.search_field.set_focus();
- $(this.search_field.$input[0]).val(sScancode).trigger("input");
- this.barcode_scanned = true;
- }
- }
- });
+ bind_events() {
+ const me = this;
+ onScan.attachTo(document, {
+ onScan: (sScancode) => {
+ if (this.search_field && this.$component.is(':visible')) {
+ this.search_field.set_focus();
+ $(this.search_field.$input[0]).val(sScancode).trigger("input");
+ this.barcode_scanned = true;
+ }
+ }
+ });
this.$component.on('click', '.item-wrapper', function() {
const $item = $(this);
const item_code = unescape($item.attr('data-item-code'));
- let batch_no = unescape($item.attr('data-batch-no'));
- let serial_no = unescape($item.attr('data-serial-no'));
- let uom = unescape($item.attr('data-uom'));
-
- // escape(undefined) returns "undefined" then unescape returns "undefined"
- batch_no = batch_no === "undefined" ? undefined : batch_no;
- serial_no = serial_no === "undefined" ? undefined : serial_no;
- uom = uom === "undefined" ? undefined : uom;
+ let batch_no = unescape($item.attr('data-batch-no'));
+ let serial_no = unescape($item.attr('data-serial-no'));
+ let uom = unescape($item.attr('data-uom'));
+
+ // escape(undefined) returns "undefined" then unescape returns "undefined"
+ batch_no = batch_no === "undefined" ? undefined : batch_no;
+ serial_no = serial_no === "undefined" ? undefined : serial_no;
+ uom = uom === "undefined" ? undefined : uom;
- me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
- })
+ me.events.item_selected({ field: 'qty', value: "+1", item: { item_code, batch_no, serial_no, uom }});
+ })
- this.search_field.$input.on('input', (e) => {
+ this.search_field.$input.on('input', (e) => {
clearTimeout(this.last_search);
this.last_search = setTimeout(() => {
const search_term = e.target.value;
this.filter_items({ search_term });
}, 300);
- });
- }
+ });
+ }
- attach_shortcuts() {
- frappe.ui.keys.on("ctrl+i", () => {
- const selector_is_visible = this.$component.is(':visible');
- if (!selector_is_visible) return;
- this.search_field.set_focus();
- });
- frappe.ui.keys.on("ctrl+g", () => {
- const selector_is_visible = this.$component.is(':visible');
- if (!selector_is_visible) return;
- this.item_group_field.set_focus();
- });
- // for selecting the last filtered item on search
- frappe.ui.keys.on("enter", () => {
- const selector_is_visible = this.$component.is(':visible');
- if (!selector_is_visible || this.search_field.get_value() === "") return;
+ attach_shortcuts() {
+ frappe.ui.keys.on("ctrl+i", () => {
+ const selector_is_visible = this.$component.is(':visible');
+ if (!selector_is_visible) return;
+ this.search_field.set_focus();
+ });
+ frappe.ui.keys.on("ctrl+g", () => {
+ const selector_is_visible = this.$component.is(':visible');
+ if (!selector_is_visible) return;
+ this.item_group_field.set_focus();
+ });
+ // for selecting the last filtered item on search
+ frappe.ui.keys.on("enter", () => {
+ const selector_is_visible = this.$component.is(':visible');
+ if (!selector_is_visible || this.search_field.get_value() === "") return;
- if (this.items.length == 1) {
- this.$items_container.find(".item-wrapper").click();
- frappe.utils.play_sound("submit");
- $(this.search_field.$input[0]).val("").trigger("input");
- } else if (this.items.length == 0 && this.barcode_scanned) {
- // only show alert of barcode is scanned and enter is pressed
- frappe.show_alert({
- message: __("No items found. Scan barcode again."),
- indicator: 'orange'
- });
- frappe.utils.play_sound("error");
- this.barcode_scanned = false;
- $(this.search_field.$input[0]).val("").trigger("input");
- }
- });
- }
-
- filter_items({ search_term='' }={}) {
+ if (this.items.length == 1) {
+ this.$items_container.find(".item-wrapper").click();
+ frappe.utils.play_sound("submit");
+ $(this.search_field.$input[0]).val("").trigger("input");
+ } else if (this.items.length == 0 && this.barcode_scanned) {
+ // only show alert of barcode is scanned and enter is pressed
+ frappe.show_alert({
+ message: __("No items found. Scan barcode again."),
+ indicator: 'orange'
+ });
+ frappe.utils.play_sound("error");
+ this.barcode_scanned = false;
+ $(this.search_field.$input[0]).val("").trigger("input");
+ }
+ });
+ }
+
+ filter_items({ search_term='' }={}) {
if (search_term) {
search_term = search_term.toLowerCase();
@@ -227,39 +227,39 @@
this.items = items;
this.render_item_list(items);
return;
- }
+ }
}
this.get_items({ search_value: search_term })
- .then(({ message }) => {
- const { items, serial_no, batch_no, barcode } = message;
+ .then(({ message }) => {
+ const { items, serial_no, batch_no, barcode } = message;
if (search_term && !barcode) {
this.search_index[search_term] = items;
}
this.items = items;
- this.render_item_list(items);
- });
+ this.render_item_list(items);
+ });
}
-
- resize_selector(minimize) {
- minimize ?
- this.$component.find('.search-field').removeClass('mr-8') :
- this.$component.find('.search-field').addClass('mr-8');
+
+ resize_selector(minimize) {
+ minimize ?
+ this.$component.find('.search-field').removeClass('mr-8') :
+ this.$component.find('.search-field').addClass('mr-8');
- minimize ?
- this.$component.find('.filter-section').addClass('flex-col') :
- this.$component.find('.filter-section').removeClass('flex-col');
+ minimize ?
+ this.$component.find('.filter-section').addClass('flex-col') :
+ this.$component.find('.filter-section').removeClass('flex-col');
- minimize ?
- this.$component.removeClass('col-span-6').addClass('col-span-2') :
- this.$component.removeClass('col-span-2').addClass('col-span-6')
+ minimize ?
+ this.$component.removeClass('col-span-6').addClass('col-span-2') :
+ this.$component.removeClass('col-span-2').addClass('col-span-6')
- minimize ?
- this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') :
- this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4')
- }
+ minimize ?
+ this.$items_container.removeClass('grid-cols-4').addClass('grid-cols-1') :
+ this.$items_container.removeClass('grid-cols-1').addClass('grid-cols-4')
+ }
- toggle_component(show) {
+ toggle_component(show) {
show ? this.$component.removeClass('d-none') : this.$component.addClass('d-none');
- }
+ }
}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js
index 2ffc2c0..4b8e841 100644
--- a/erpnext/selling/page/point_of_sale/pos_number_pad.js
+++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js
@@ -1,49 +1,48 @@
erpnext.PointOfSale.NumberPad = class {
- constructor({ wrapper, events, cols, keys, css_classes, fieldnames_map }) {
- this.wrapper = wrapper;
- this.events = events;
- this.cols = cols;
- this.keys = keys;
- this.css_classes = css_classes || [];
- this.fieldnames = fieldnames_map || {};
+ constructor({ wrapper, events, cols, keys, css_classes, fieldnames_map }) {
+ this.wrapper = wrapper;
+ this.events = events;
+ this.cols = cols;
+ this.keys = keys;
+ this.css_classes = css_classes || [];
+ this.fieldnames = fieldnames_map || {};
- this.init_component();
- }
+ this.init_component();
+ }
- init_component() {
- this.prepare_dom();
- this.bind_events();
- }
+ init_component() {
+ this.prepare_dom();
+ this.bind_events();
+ }
- prepare_dom() {
- const { cols, keys, css_classes, fieldnames } = this;
+ prepare_dom() {
+ const { cols, keys, css_classes, fieldnames } = this;
- function get_keys() {
- return keys.reduce((a, row, i) => {
- return a + row.reduce((a2, number, j) => {
- const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : '';
- const fieldname = fieldnames && fieldnames[number] ?
- fieldnames[number] :
- typeof number === 'string' ? frappe.scrub(number) : number;
-
- return a2 + `<div class="numpad-btn pointer no-select rounded ${class_to_append}
- flex items-center justify-center h-16 text-md border-grey border" data-button-value="${fieldname}">${number}</div>`
- }, '')
- }, '');
- }
+ function get_keys() {
+ return keys.reduce((a, row, i) => {
+ return a + row.reduce((a2, number, j) => {
+ const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : '';
+ const fieldname = fieldnames && fieldnames[number] ?
+ fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number;
- this.wrapper.html(
- `<div class="grid grid-cols-${cols} gap-4">
- ${get_keys()}
- </div>`
- )
- }
+ return a2 + `<div class="numpad-btn pointer no-select rounded ${class_to_append}
+ flex items-center justify-center h-16 text-md border-grey border" data-button-value="${fieldname}">${number}</div>`
+ }, '')
+ }, '');
+ }
- bind_events() {
- const me = this;
- this.wrapper.on('click', '.numpad-btn', function() {
- const $btn = $(this);
- me.events.numpad_event($btn);
- })
- }
+ this.wrapper.html(
+ `<div class="grid grid-cols-${cols} gap-4">
+ ${get_keys()}
+ </div>`
+ )
+ }
+
+ bind_events() {
+ const me = this;
+ this.wrapper.on('click', '.numpad-btn', function() {
+ const $btn = $(this);
+ me.events.numpad_event($btn);
+ });
+ }
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index 60f6a68..19d0bec 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -130,12 +130,18 @@
if (this.frm.doc.docstatus===0) {
this.frm.add_custom_button(__('Sales Order'),
function() {
+ if (!me.frm.doc.customer) {
+ frappe.throw({
+ title: __("Mandatory"),
+ message: __("Please Select a Customer")
+ });
+ }
erpnext.utils.map_current_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
source_doctype: "Sales Order",
target: me.frm,
setters: {
- customer: me.frm.doc.customer || undefined,
+ customer: me.frm.doc.customer,
},
get_query_filters: {
docstatus: 1,
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 50c18f6..c504e23 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -116,12 +116,18 @@
if (this.frm.doc.docstatus == 0) {
this.frm.add_custom_button(__('Purchase Order'),
function () {
+ if (!me.frm.doc.supplier) {
+ frappe.throw({
+ title: __("Mandatory"),
+ message: __("Please Select a Supplier")
+ });
+ }
erpnext.utils.map_current_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt",
source_doctype: "Purchase Order",
target: me.frm,
setters: {
- supplier: me.frm.doc.supplier || undefined,
+ supplier: me.frm.doc.supplier,
},
get_query_filters: {
docstatus: 1,
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index c951066..3643174 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -73,7 +73,8 @@
"fieldname": "reference_type",
"fieldtype": "Select",
"label": "Reference Type",
- "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry"
+ "options": "\nPurchase Receipt\nPurchase Invoice\nDelivery Note\nSales Invoice\nStock Entry",
+ "reqd": 1
},
{
"fieldname": "reference_name",
@@ -84,7 +85,8 @@
"label": "Reference Name",
"oldfieldname": "purchase_receipt_no",
"oldfieldtype": "Link",
- "options": "reference_type"
+ "options": "reference_type",
+ "reqd": 1
},
{
"fieldname": "section_break_7",
@@ -231,9 +233,10 @@
],
"icon": "fa fa-search",
"idx": 1,
+ "index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-26 17:50:25.068222",
+ "modified": "2020-09-12 16:11:31.910508",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection",