Merge branch 'accounts_dashboard' of https://github.com/deepeshgarg007/erpnext into deepeshgarg007-accounts_dashboard
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index b0210e5..b57e678 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -185,7 +185,7 @@
total_days, total_booking_days, account_currency)
make_gl_entries(doc, credit_account, debit_account, against,
- amount, base_amount, end_date, project, account_currency, item.cost_center, item.name, deferred_process)
+ amount, base_amount, end_date, project, account_currency, item.cost_center, item, deferred_process)
# Returned in case of any errors because it tries to submit the same record again and again in case of errors
if frappe.flags.deferred_accounting_error:
@@ -222,7 +222,7 @@
doc.submit()
def make_gl_entries(doc, credit_account, debit_account, against,
- amount, base_amount, posting_date, project, account_currency, cost_center, voucher_detail_no, deferred_process=None):
+ amount, base_amount, posting_date, project, account_currency, cost_center, item, deferred_process=None):
# GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries
@@ -236,12 +236,12 @@
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": cost_center,
- "voucher_detail_no": voucher_detail_no,
+ "voucher_detail_no": item.name,
'posting_date': posting_date,
'project': project,
'against_voucher_type': 'Process Deferred Accounting',
'against_voucher': deferred_process
- }, account_currency)
+ }, account_currency, item=item)
)
# GL Entry to debit the amount from the expense
gl_entries.append(
@@ -251,12 +251,12 @@
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": cost_center,
- "voucher_detail_no": voucher_detail_no,
+ "voucher_detail_no": item.name,
'posting_date': posting_date,
'project': project,
'against_voucher_type': 'Process Deferred Accounting',
'against_voucher': deferred_process
- }, account_currency)
+ }, account_currency, item=item)
)
if gl_entries:
diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
index 7a85bfb..894ec5b 100644
--- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
+++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py
@@ -162,9 +162,9 @@
def get_doctypes_with_dimensions():
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
- "Expense Claim", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note", "Sales Invoice Item", "Purchase Invoice Item",
- "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item", "Purchase Receipt Item",
- "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
+ "Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
+ "Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
+ "Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
"Subscription Plan"]
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index efab580..291aff3 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -112,8 +112,8 @@
from tabAccount where name=%s""", self.account, as_dict=1)[0]
if ret.is_group==1:
- frappe.throw(_("{0} {1}: Account {2} cannot be a Group")
- .format(self.voucher_type, self.voucher_no, self.account))
+ frappe.throw(_('''{0} {1}: Account {2} is a Group Account and group accounts cannot be used in
+ transactions''').format(self.voucher_type, self.voucher_no, self.account))
if ret.docstatus==2:
frappe.throw(_("{0} {1}: Account {2} is inactive")
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 39fc203..594b4d4 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -8,6 +8,7 @@
from frappe.utils import flt, getdate, nowdate, add_days
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries
+from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions
class InvoiceDiscounting(AccountsController):
def validate(self):
@@ -81,10 +82,15 @@
def make_gl_entries(self):
company_currency = frappe.get_cached_value('Company', self.company, "default_currency")
+
gl_entries = []
+ invoice_fields = ["debit_to", "party_account_currency", "conversion_rate", "cost_center"]
+ accounting_dimensions = get_accounting_dimensions()
+
+ invoice_fields.extend(accounting_dimensions)
+
for d in self.invoices:
- inv = frappe.db.get_value("Sales Invoice", d.sales_invoice,
- ["debit_to", "party_account_currency", "conversion_rate", "cost_center"], as_dict=1)
+ inv = frappe.db.get_value("Sales Invoice", d.sales_invoice, invoice_fields, as_dict=1)
if d.outstanding_amount:
outstanding_in_company_currency = flt(d.outstanding_amount * inv.conversion_rate,
@@ -102,7 +108,7 @@
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
- }, inv.party_account_currency))
+ }, inv.party_account_currency, item=inv))
gl_entries.append(self.get_gl_dict({
"account": self.accounts_receivable_credit,
@@ -115,7 +121,7 @@
"cost_center": inv.cost_center,
"against_voucher": d.sales_invoice,
"against_voucher_type": "Sales Invoice"
- }, ar_credit_account_currency))
+ }, ar_credit_account_currency, item=inv))
make_gl_entries(gl_entries, cancel=(self.docstatus == 2), update_outstanding='No')
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 83c670e..d2245d6 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -86,7 +86,7 @@
self.update_payment_schedule(cancel=1)
self.set_payment_req_status()
self.set_status()
-
+
def set_payment_req_status(self):
from erpnext.accounts.doctype.payment_request.payment_request import update_payment_req_status
update_payment_req_status(self, None)
@@ -280,7 +280,7 @@
outstanding_amount, is_return = frappe.get_cached_value(d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"])
if outstanding_amount <= 0 and not is_return:
no_oustanding_refs.setdefault(d.reference_doctype, []).append(d)
-
+
for k, v in no_oustanding_refs.items():
frappe.msgprint(_("{} - {} now have {} as they had no outstanding amount left before submitting the Payment Entry.<br><br>\
If this is undesirable please cancel the corresponding Payment Entry.")
@@ -451,8 +451,6 @@
frappe.throw(_("Reference No and Reference Date is mandatory for Bank transaction"))
def set_remarks(self):
- if self.remarks: return
-
if self.payment_type=="Internal Transfer":
remarks = [_("Amount {0} {1} transferred from {2} to {3}")
.format(self.paid_from_account_currency, self.paid_amount, self.paid_from, self.paid_to)]
@@ -506,7 +504,7 @@
"against": against_account,
"account_currency": self.party_account_currency,
"cost_center": self.cost_center
- })
+ }, item=self)
dr_or_cr = "credit" if erpnext.get_party_account_type(self.party_type) == 'Receivable' else "debit"
@@ -550,7 +548,7 @@
"credit_in_account_currency": self.paid_amount,
"credit": self.base_paid_amount,
"cost_center": self.cost_center
- })
+ }, item=self)
)
if self.payment_type in ("Receive", "Internal Transfer"):
gl_entries.append(
@@ -561,7 +559,7 @@
"debit_in_account_currency": self.received_amount,
"debit": self.base_received_amount,
"cost_center": self.cost_center
- })
+ }, item=self)
)
def add_deductions_gl_entries(self, gl_entries):
diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
index 19f571f..4d9053a 100644
--- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
+++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py
@@ -99,7 +99,7 @@
self.same_item = 1
def validate_max_discount(self):
- if self.rate_or_discount == "Discount Percentage" and self.items:
+ if self.rate_or_discount == "Discount Percentage" and self.get("items"):
for d in self.items:
max_discount = frappe.get_cached_value("Item", d.item_code, "max_discount")
if max_discount and flt(self.discount_percentage) > flt(max_discount):
diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py
index b358f56..cb05481 100644
--- a/erpnext/accounts/doctype/pricing_rule/utils.py
+++ b/erpnext/accounts/doctype/pricing_rule/utils.py
@@ -4,13 +4,19 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-import frappe, copy, json
-from frappe import throw, _
+
+import copy
+import json
+
from six import string_types
-from frappe.utils import flt, cint, get_datetime, get_link_to_form, today
+
+import frappe
from erpnext.setup.doctype.item_group.item_group import get_child_item_groups
from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses
from erpnext.stock.get_item_details import get_conversion_factor
+from frappe import _, throw
+from frappe.utils import cint, flt, get_datetime, get_link_to_form, getdate, today
+
class MultiplePricingRuleConflict(frappe.ValidationError): pass
@@ -502,18 +508,16 @@
return list(set(apply_on_data))
def validate_coupon_code(coupon_name):
- from frappe.utils import today,getdate
- coupon=frappe.get_doc("Coupon Code",coupon_name)
+ coupon = frappe.get_doc("Coupon Code", coupon_name)
+
if coupon.valid_from:
- if coupon.valid_from > getdate(today()) :
- frappe.throw(_("Sorry,coupon code validity has not started"))
+ if coupon.valid_from > getdate(today()):
+ frappe.throw(_("Sorry, this coupon code's validity has not started"))
elif coupon.valid_upto:
- if coupon.valid_upto < getdate(today()) :
- frappe.throw(_("Sorry,coupon code validity has expired"))
- elif coupon.used>=coupon.maximum_use:
- frappe.throw(_("Sorry,coupon code are exhausted"))
- else:
- return
+ if coupon.valid_upto < getdate(today()):
+ frappe.throw(_("Sorry, this coupon code's validity has expired"))
+ elif coupon.used >= coupon.maximum_use:
+ frappe.throw(_("Sorry, this coupon code is no longer valid"))
def update_coupon_code_count(coupon_name,transaction_type):
coupon=frappe.get_doc("Coupon Code",coupon_name)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index 3aa24df..aa1d5b5 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -460,7 +460,7 @@
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
def make_item_gl_entries(self, gl_entries):
@@ -841,7 +841,7 @@
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
gl_entries.append(
@@ -852,7 +852,7 @@
"credit_in_account_currency": self.base_paid_amount \
if bank_account_currency==self.company_currency else self.paid_amount,
"cost_center": self.cost_center
- }, bank_account_currency)
+ }, bank_account_currency, item=self)
)
def make_write_off_gl_entry(self, gl_entries):
@@ -873,7 +873,7 @@
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
gl_entries.append(
self.get_gl_dict({
@@ -883,7 +883,7 @@
"credit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount,
"cost_center": self.cost_center or self.write_off_cost_center
- })
+ }, item=self)
)
def make_gle_for_rounding_adjustment(self, gl_entries):
@@ -902,8 +902,7 @@
"debit_in_account_currency": self.rounding_adjustment,
"debit": self.base_rounding_adjustment,
"cost_center": self.cost_center or round_off_cost_center,
- }
- ))
+ }, item=self))
def on_cancel(self):
super(PurchaseInvoice, self).on_cancel()
@@ -1021,6 +1020,40 @@
# calculate totals again after applying TDS
self.calculate_taxes_and_totals()
+
+ def set_status(self, update=False, status=None, update_modified=True):
+ if self.is_new():
+ if self.get('amended_from'):
+ self.status = 'Draft'
+ return
+
+ precision = self.precision("outstanding_amount")
+ outstanding_amount = flt(self.outstanding_amount, precision)
+ due_date = getdate(self.due_date)
+ nowdate = getdate()
+
+ if not status:
+ if self.docstatus == 2:
+ status = "Cancelled"
+ elif self.docstatus == 1:
+ if outstanding_amount > 0 and due_date < nowdate:
+ self.status = "Overdue"
+ elif outstanding_amount > 0 and due_date >= nowdate:
+ self.status = "Unpaid"
+ #Check if outstanding amount is 0 due to debit note issued against invoice
+ elif outstanding_amount <= 0 and self.is_return == 0 and frappe.db.get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1}):
+ self.status = "Debit Note Issued"
+ elif self.is_return == 1:
+ self.status = "Return"
+ elif outstanding_amount<=0:
+ self.status = "Paid"
+ else:
+ self.status = "Submitted"
+ else:
+ self.status = "Draft"
+
+ if update:
+ self.db_set('status', self.status, update_modified = update_modified)
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index e41ad42..6170005 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -86,6 +86,8 @@
pe.submit()
pi_doc = frappe.get_doc('Purchase Invoice', pi_doc.name)
+ pi_doc.load_from_db()
+ self.assertTrue(pi_doc.status, "Paid")
self.assertRaises(frappe.LinkExistsError, pi_doc.cancel)
unlink_payment_on_cancel_of_invoice()
@@ -203,7 +205,9 @@
pi.insert()
pi.submit()
+ pi.load_from_db()
+ self.assertTrue(pi.status, "Unpaid")
self.check_gle_for_pi(pi.name)
def check_gle_for_pi(self, pi):
@@ -234,6 +238,9 @@
pi = frappe.copy_doc(test_records[0])
pi.insert()
+ pi.load_from_db()
+
+ self.assertTrue(pi.status, "Draft")
pi.naming_series = 'TEST-'
self.assertRaises(frappe.CannotChangeConstantError, pi.save)
@@ -248,6 +255,8 @@
pi.get("taxes").pop(1)
pi.insert()
pi.submit()
+ pi.load_from_db()
+ self.assertTrue(pi.status, "Unpaid")
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
@@ -599,6 +608,11 @@
# return entry
pi1 = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2, rate=50, update_stock=1)
+ pi.load_from_db()
+ self.assertTrue(pi.status, "Debit Note Issued")
+ pi1.load_from_db()
+ self.assertTrue(pi1.status, "Return")
+
actual_qty_2 = get_qty_after_transaction()
self.assertEqual(actual_qty_1 - 2, actual_qty_2)
@@ -771,6 +785,8 @@
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import get_outstanding_amount
pi = make_purchase_invoice(item_code = "_Test Item", qty = (5 * -1), rate=500, is_return = 1)
+ pi.load_from_db()
+ self.assertTrue(pi.status, "Return")
outstanding_amount = get_outstanding_amount(pi.doctype,
pi.name, "Creditors - _TC", pi.supplier, "Supplier")
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index f248276..df0c3d2 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -924,7 +924,7 @@
if(patient && patient!=selected_patient){
selected_patient = patient;
var method = "erpnext.healthcare.utils.get_healthcare_services_to_invoice";
- var args = {patient: patient};
+ var args = {patient: patient, company: frm.doc.company};
var columns = (["service", "reference_name", "reference_type"]);
get_healthcare_items(frm, true, $results, $placeholder, method, args, columns);
}
@@ -1068,7 +1068,11 @@
description:'Quantity will be calculated only for items which has "Nos" as UoM. You may change as required for each invoice item.',
get_query: function(doc) {
return {
- filters: { patient: dialog.get_value("patient"), docstatus: 1 }
+ filters: {
+ patient: dialog.get_value("patient"),
+ company: frm.doc.company,
+ docstatus: 1
+ }
};
}
},
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 3b0fade..05b85da 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -791,7 +791,7 @@
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
def make_tax_gl_entries(self, gl_entries):
@@ -808,7 +808,7 @@
tax.precision("base_tax_amount_after_discount_amount")) if account_currency==self.company_currency else
flt(tax.tax_amount_after_discount_amount, tax.precision("tax_amount_after_discount_amount"))),
"cost_center": tax.cost_center
- }, account_currency)
+ }, account_currency, item=tax)
)
def make_item_gl_entries(self, gl_entries):
@@ -828,7 +828,7 @@
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
- gl_entries.append(self.get_gl_dict(gle))
+ gl_entries.append(self.get_gl_dict(gle, item=item))
asset.db_set("disposal_date", self.posting_date)
asset.set_status("Sold" if self.docstatus==1 else None)
@@ -866,7 +866,7 @@
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- })
+ }, item=self)
)
gl_entries.append(
self.get_gl_dict({
@@ -875,7 +875,7 @@
"against": self.customer,
"debit": self.loyalty_amount,
"remark": "Loyalty Points redeemed by the customer"
- })
+ }, item=self)
)
def make_pos_gl_entries(self, gl_entries):
@@ -896,7 +896,7 @@
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
payment_mode_account_currency = get_account_currency(payment_mode.account)
@@ -909,7 +909,7 @@
if payment_mode_account_currency==self.company_currency \
else payment_mode.amount,
"cost_center": self.cost_center
- }, payment_mode_account_currency)
+ }, payment_mode_account_currency, item=self)
)
def make_gle_for_change_amount(self, gl_entries):
@@ -927,7 +927,7 @@
"against_voucher": self.return_against if cint(self.is_return) and self.return_against else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
gl_entries.append(
@@ -936,7 +936,7 @@
"against": self.customer,
"credit": self.base_change_amount,
"cost_center": self.cost_center
- })
+ }, item=self)
)
else:
frappe.throw(_("Select change amount account"), title="Mandatory Field")
@@ -960,7 +960,7 @@
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center
- }, self.party_account_currency)
+ }, self.party_account_currency, item=self)
)
gl_entries.append(
self.get_gl_dict({
@@ -971,7 +971,7 @@
self.precision("base_write_off_amount")) if write_off_account_currency==self.company_currency
else flt(self.write_off_amount, self.precision("write_off_amount"))),
"cost_center": self.cost_center or self.write_off_cost_center or default_cost_center
- }, write_off_account_currency)
+ }, write_off_account_currency, item=self)
)
def make_gle_for_rounding_adjustment(self, gl_entries):
@@ -988,8 +988,7 @@
"credit": flt(self.base_rounding_adjustment,
self.precision("base_rounding_adjustment")),
"cost_center": self.cost_center or round_off_cost_center,
- }
- ))
+ }, item=self))
def update_billing_status_in_dn(self, update_modified=True):
updated_delivery_notes = []
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index dd6b4fd..4d43919 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -58,7 +58,7 @@
"rate": tax_rate_detail.tax_withholding_rate,
"threshold": tax_rate_detail.single_threshold,
"cumulative_threshold": tax_rate_detail.cumulative_threshold,
- "description": tax_withholding.category_name
+ "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category
})
def get_tax_withholding_rates(tax_withholding, fiscal_year):
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index 622bab6..07752e1 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -46,7 +46,7 @@
"default": frappe.defaults.get_user_default("year_end_date"),
},
{
- "fieldname":"cost_center",
+ "fieldname": "cost_center",
"label": __("Cost Center"),
"fieldtype": "Link",
"options": "Cost Center",
@@ -61,7 +61,13 @@
}
},
{
- "fieldname":"finance_book",
+ "fieldname": "project",
+ "label": __("Project"),
+ "fieldtype": "Link",
+ "options": "Project"
+ },
+ {
+ "fieldname": "finance_book",
"label": __("Finance Book"),
"fieldtype": "Link",
"options": "Finance Book",
@@ -97,7 +103,7 @@
}
erpnext.dimension_filters.forEach((dimension) => {
- frappe.query_reports["Trial Balance"].filters.splice(5, 0 ,{
+ frappe.query_reports["Trial Balance"].filters.splice(6, 0 ,{
"fieldname": dimension["fieldname"],
"label": __(dimension["label"]),
"fieldtype": "Link",
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index d783241..8bd4399 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -69,6 +69,10 @@
gl_entries_by_account = {}
opening_balances = get_opening_balances(filters)
+
+ #add filter inside list so that the query in financial_statements.py doesn't break
+ filters.project = [filters.project]
+
set_gl_entries_by_account(filters.company, filters.from_date,
filters.to_date, min_lft, max_rgt, filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry))
@@ -102,6 +106,9 @@
additional_conditions += """ and cost_center in (select name from `tabCost Center`
where lft >= %s and rgt <= %s)""" % (lft, rgt)
+ if filters.project:
+ additional_conditions += " and project = %(project)s"
+
if filters.finance_book:
fb_conditions = " AND finance_book = %(finance_book)s"
if filters.include_default_book_entries:
@@ -116,6 +123,7 @@
"from_date": filters.from_date,
"report_type": report_type,
"year_start_date": filters.year_start_date,
+ "project": filters.project,
"finance_book": filters.finance_book,
"company_fb": frappe.db.get_value("Company", filters.company, 'default_finance_book')
}
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index a3200d5..505ba4c 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -125,7 +125,7 @@
if self.available_for_use_date and getdate(self.available_for_use_date) < getdate(self.purchase_date):
frappe.throw(_("Available-for-use Date should be after purchase date"))
-
+
def validate_gross_and_purchase_amount(self):
if self.gross_purchase_amount and self.gross_purchase_amount != self.purchase_receipt_amount:
frappe.throw(_("Gross Purchase Amount should be {} to purchase amount of one single Asset. {}\
@@ -455,7 +455,7 @@
for d in self.get('finance_books'):
if d.finance_book == self.default_finance_book:
return cint(d.idx) - 1
-
+
def validate_make_gl_entry(self):
purchase_document = self.get_purchase_document()
asset_bought_with_invoice = purchase_document == self.purchase_invoice
@@ -487,14 +487,14 @@
purchase_document = self.purchase_invoice if asset_bought_with_invoice else self.purchase_receipt
return purchase_document
-
+
def get_asset_accounts(self):
fixed_asset_account = get_asset_category_account('fixed_asset_account', asset=self.name,
asset_category = self.asset_category, company = self.company)
cwip_account = get_asset_account("capital_work_in_progress_account",
self.name, self.asset_category, self.company)
-
+
return fixed_asset_account, cwip_account
def make_gl_entries(self):
@@ -513,7 +513,7 @@
"credit": self.purchase_receipt_amount,
"credit_in_account_currency": self.purchase_receipt_amount,
"cost_center": self.cost_center
- }))
+ }, item=self))
gl_entries.append(self.get_gl_dict({
"account": fixed_asset_account,
@@ -523,7 +523,7 @@
"debit": self.purchase_receipt_amount,
"debit_in_account_currency": self.purchase_receipt_amount,
"cost_center": self.cost_center
- }))
+ }, item=self))
if gl_entries:
from erpnext.accounts.general_ledger import make_gl_entries
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
index 16061c6..1b8b404 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js
@@ -18,6 +18,10 @@
refresh: function() {
var me = this;
this._super();
+
+ if (this.frm.doc.__islocal && !this.frm.doc.valid_till) {
+ this.frm.set_value('valid_till', frappe.datetime.add_months(this.frm.doc.transaction_date, 1));
+ }
if (this.frm.doc.docstatus === 1) {
cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order,
__('Create'));
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 82fc628..3bc441a 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -13,9 +13,10 @@
"supplier",
"supplier_name",
"column_break1",
- "transaction_date",
- "amended_from",
"company",
+ "transaction_date",
+ "valid_till",
+ "amended_from",
"address_section",
"supplier_address",
"contact_person",
@@ -791,13 +792,18 @@
"options": "Opportunity",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "valid_till",
+ "fieldtype": "Date",
+ "label": "Valid Till"
}
],
"icon": "fa fa-shopping-cart",
"idx": 29,
"is_submittable": 1,
"links": [],
- "modified": "2019-12-30 19:17:28.208693",
+ "modified": "2020-04-15 11:44:52.958022",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
index 5b4356a..baf2457 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py
@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
-from frappe.utils import flt, nowdate, add_days
+from frappe.utils import flt, nowdate, add_days, getdate
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.buying_controller import BuyingController
@@ -28,6 +28,7 @@
validate_for_items(self)
self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty")
+ self.validate_valid_till()
def on_submit(self):
frappe.db.set(self, "status", "Submitted")
@@ -52,6 +53,11 @@
"is_child_table": True
}
})
+
+ def validate_valid_till(self):
+ if self.valid_till and getdate(self.valid_till) < getdate(self.transaction_date):
+ frappe.throw(_("Valid till Date cannot be before Transaction Date"))
+
def update_rfq_supplier_status(self, include_me):
rfq_list = set([])
for item in self.items:
@@ -158,3 +164,11 @@
}, target_doc)
return doclist
+
+def set_expired_status():
+ frappe.db.sql("""
+ UPDATE
+ `tabSupplier Quotation` SET `status` = 'Expired'
+ WHERE
+ `status` not in ('Cancelled', 'Stopped') AND `valid_till` < %s
+ """, (nowdate()))
\ No newline at end of file
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js
index 9555439..9f4fece 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js
@@ -5,6 +5,8 @@
return [__("Ordered"), "green", "status,=,Ordered"];
} else if(doc.status==="Rejected") {
return [__("Lost"), "darkgrey", "status,=,Lost"];
+ } else if(doc.status==="Expired") {
+ return [__("Expired"), "darkgrey", "status,=,Expired"];
}
}
};
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
index 3d05612..a76ffee 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.js
@@ -5,20 +5,18 @@
filters: [
{
fieldtype: "Link",
- label: __("Supplier Quotation"),
- options: "Supplier Quotation",
- fieldname: "supplier_quotation",
- default: "",
- get_query: () => {
- return { filters: { "docstatus": ["<", 2] } }
- }
+ label: __("Company"),
+ options: "Company",
+ fieldname: "company",
+ default: frappe.defaults.get_user_default("Company"),
+ "reqd": 1
},
{
reqd: 1,
default: "",
options: "Item",
label: __("Item"),
- fieldname: "item",
+ fieldname: "item_code",
fieldtype: "Link",
get_query: () => {
let quote = frappe.query_report.get_filter_value('supplier_quotation');
@@ -37,8 +35,37 @@
}
}
}
+ },
+ {
+ fieldname: "supplier",
+ label: __("Supplier"),
+ fieldtype: "MultiSelectList",
+ get_data: function(txt) {
+ return frappe.db.get_link_options('Supplier', txt);
+ }
+ },
+ {
+ fieldtype: "Link",
+ label: __("Supplier Quotation"),
+ options: "Supplier Quotation",
+ fieldname: "supplier_quotation",
+ default: "",
+ get_query: () => {
+ return { filters: { "docstatus": ["<", 2] } }
+ }
+ },
+ {
+ fieldtype: "Link",
+ label: __("Request for Quotation"),
+ options: "Request for Quotation",
+ fieldname: "request_for_quotation",
+ default: "",
+ get_query: () => {
+ return { filters: { "docstatus": ["<", 2] } }
+ }
}
],
+
onload: (report) => {
// Create a button for setting the default supplier
report.page.add_inner_button(__("Select Default Supplier"), () => {
@@ -102,6 +129,4 @@
});
dialog.show();
}
-}
-
-
+}
\ No newline at end of file
diff --git a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
index 5aff6ba..a33867a 100644
--- a/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
+++ b/erpnext/buying/report/quoted_item_comparison/quoted_item_comparison.py
@@ -2,103 +2,180 @@
# For license information, please see license.txt
from __future__ import unicode_literals
-from erpnext.setup.utils import get_exchange_rate
-from frappe.utils import flt, cint
import frappe
+from frappe.utils import flt, cint
+from frappe import _
+from collections import defaultdict
+from erpnext.setup.utils import get_exchange_rate
def execute(filters=None):
- qty_list = get_quantity_list(filters.item)
- data = get_quote_list(filters.item, qty_list)
- columns = get_columns(qty_list)
- return columns, data
-
-def get_quote_list(item, qty_list):
- out = []
- if not item:
+ if not filters:
+ return [], []
+
+ conditions = get_conditions(filters)
+ supplier_quotation_data = get_data(filters, conditions)
+ columns = get_columns()
+
+ data, chart_data = prepare_data(supplier_quotation_data)
+
+ return columns, data, None, chart_data
+
+def get_conditions(filters):
+ conditions = ""
+ if filters.get("supplier_quotation"):
+ conditions += " AND sqi.parent = %(supplier_quotation)s"
+
+ if filters.get("request_for_quotation"):
+ conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s"
+
+ if filters.get("supplier"):
+ conditions += " AND sq.supplier in %(supplier)s"
+ return conditions
+
+def get_data(filters, conditions):
+ if not filters.get("item_code"):
return []
- suppliers = []
- price_data = []
- company_currency = frappe.db.get_default("currency")
- float_precision = cint(frappe.db.get_default("float_precision")) or 2
- # Get the list of suppliers
- for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
- where item_code=%s and docstatus < 2""", item, as_dict=1):
- for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
- where name =%s and docstatus < 2""", root.parent, as_dict=1):
- ip = frappe._dict({
- "supplier": splr.supplier,
- "qty": root.qty,
- "parent": root.parent,
- "rate": root.rate
- })
- price_data.append(ip)
- suppliers.append(splr.supplier)
+ supplier_quotation_data = frappe.db.sql("""SELECT
+ sqi.parent, sqi.qty, sqi.rate, sqi.uom, sqi.request_for_quotation,
+ sq.supplier
+ FROM
+ `tabSupplier Quotation Item` sqi,
+ `tabSupplier Quotation` sq
+ WHERE
+ sqi.item_code = %(item_code)s
+ AND sqi.parent = sq.name
+ AND sqi.docstatus < 2
+ AND sq.company = %(company)s
+ AND sq.status != 'Expired'
+ {0}""".format(conditions), filters, as_dict=1)
- #Add a row for each supplier
- for root in set(suppliers):
- supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
+ return supplier_quotation_data
+
+def prepare_data(supplier_quotation_data):
+ out, suppliers, qty_list = [], [], []
+ supplier_wise_map = defaultdict(list)
+ supplier_qty_price_map = {}
+
+ company_currency = frappe.db.get_default("currency")
+ float_precision = cint(frappe.db.get_default("float_precision")) or 2
+
+ for data in supplier_quotation_data:
+ supplier = data.get("supplier")
+ supplier_currency = frappe.db.get_value("Supplier", data.get("supplier"), "default_currency")
+
if supplier_currency:
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
else:
exchange_rate = 1
- row = frappe._dict({
- "supplier_name": root
- })
- for col in qty_list:
- # Get the quantity for this row
- for item_price in price_data:
- if str(item_price.qty) == col.key and item_price.supplier == root:
- row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
- row[col.key + "QUOTE"] = item_price.parent
- break
- else:
- row[col.key] = ""
- row[col.key + "QUOTE"] = ""
- out.append(row)
-
- return out
-
-def get_quantity_list(item):
- out = []
-
- if item:
- qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
- where ifnull(item_code,'')=%s and docstatus < 2 order by qty""", item, as_dict=1)
+ row = {
+ "quotation": data.get("parent"),
+ "qty": data.get("qty"),
+ "price": flt(data.get("rate") * exchange_rate, float_precision),
+ "uom": data.get("uom"),
+ "request_for_quotation": data.get("request_for_quotation"),
+ }
- for qt in qty_list:
- col = frappe._dict({
- "key": str(qt.qty),
- "label": "Qty: " + str(int(qt.qty))
- })
- out.append(col)
+ # map for report view of form {'supplier1':[{},{},...]}
+ supplier_wise_map[supplier].append(row)
- return out
-
-def get_columns(qty_list):
+ # map for chart preparation of the form {'supplier1': {'qty': 'price'}}
+ if not supplier in supplier_qty_price_map:
+ supplier_qty_price_map[supplier] = {}
+ supplier_qty_price_map[supplier][row["qty"]] = row["price"]
+
+ suppliers.append(supplier)
+ qty_list.append(data.get("qty"))
+
+ suppliers = list(set(suppliers))
+ qty_list = list(set(qty_list))
+
+ # final data format for report view
+ for supplier in suppliers:
+ supplier_wise_map[supplier][0].update({"supplier_name": supplier})
+ for entry in supplier_wise_map[supplier]:
+ out.append(entry)
+
+ chart_data = prepare_chart_data(suppliers, qty_list, supplier_qty_price_map)
+
+ return out, chart_data
+
+def prepare_chart_data(suppliers, qty_list, supplier_qty_price_map):
+ data_points_map = {}
+ qty_list.sort()
+
+ # create qty wise values map of the form {'qty1':[value1, value2]}
+ for supplier in suppliers:
+ entry = supplier_qty_price_map[supplier]
+ for qty in qty_list:
+ if not qty in data_points_map:
+ data_points_map[qty] = []
+ if qty in entry:
+ data_points_map[qty].append(entry[qty])
+ else:
+ data_points_map[qty].append(None)
+
+ dataset = []
+ for qty in qty_list:
+ datapoints = {
+ "name": _("Price for Qty ") + str(qty),
+ "values": data_points_map[qty]
+ }
+ dataset.append(datapoints)
+
+ chart_data = {
+ "data": {
+ "labels": suppliers,
+ "datasets": dataset
+ },
+ "type": "bar"
+ }
+
+ return chart_data
+
+def get_columns():
columns = [{
"fieldname": "supplier_name",
- "label": "Supplier",
+ "label": _("Supplier"),
"fieldtype": "Link",
"options": "Supplier",
"width": 200
- }]
-
- for qty in qty_list:
- columns.append({
- "fieldname": qty.key,
- "label": qty.label,
- "fieldtype": "Currency",
- "options": "currency",
- "width": 80
- })
- columns.append({
- "fieldname": qty.key + "QUOTE",
- "label": "Quotation",
- "fieldtype": "Link",
- "options": "Supplier Quotation",
- "width": 90
- })
+ },
+ {
+ "fieldname": "quotation",
+ "label": _("Supplier Quotation"),
+ "fieldtype": "Link",
+ "options": "Supplier Quotation",
+ "width": 200
+ },
+ {
+ "fieldname": "qty",
+ "label": _("Quantity"),
+ "fieldtype": "Float",
+ "width": 80
+ },
+ {
+ "fieldname": "price",
+ "label": _("Price"),
+ "fieldtype": "Currency",
+ "options": "Company:company:default_currency",
+ "width": 110
+ },
+ {
+ "fieldname": "uom",
+ "label": _("UOM"),
+ "fieldtype": "Link",
+ "options": "UOM",
+ "width": 90
+ },
+ {
+ "fieldname": "request_for_quotation",
+ "label": _("Request for Quotation"),
+ "fieldtype": "Link",
+ "options": "Request for Quotation",
+ "width": 200
+ }
+ ]
return columns
\ No newline at end of file
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index de76e45..b465a10 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -69,17 +69,6 @@
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
- "Purchase Invoice": [
- ["Draft", None],
- ["Submitted", "eval:self.docstatus==1"],
- ["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
- ["Return", "eval:self.is_return==1 and self.docstatus==1"],
- ["Debit Note Issued",
- "eval:self.outstanding_amount <= 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
- ["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
- ["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
- ["Cancelled", "eval:self.docstatus==2"],
- ],
"Material Request": [
["Draft", None],
["Stopped", "eval:self.status == 'Stopped'"],
diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json
index 20ab51d..6fef0c4 100644
--- a/erpnext/crm/doctype/lead/lead.json
+++ b/erpnext/crm/doctype/lead/lead.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"allow_events_in_timeline": 1,
"allow_import": 1,
"autoname": "naming_series:",
@@ -447,7 +448,7 @@
"idx": 5,
"image_field": "image",
"links": [],
- "modified": "2020-04-08 22:26:11.687110",
+ "modified": "2020-05-11 20:27:45.868960",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",
@@ -504,15 +505,6 @@
"read": 1,
"report": 1,
"role": "Sales User"
- },
- {
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Guest",
- "share": 1
}
],
"search_fields": "lead_name,lead_owner,status",
diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
index bdde9ee..377e061 100644
--- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
+++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.py
@@ -15,7 +15,7 @@
params = urlencode({
"response_type":"code",
"client_id": self.consumer_key,
- "redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?",
+ "redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
"scope": "r_emailaddress w_organization_social r_basicprofile r_liteprofile r_organization_social rw_organization_admin w_member_social"
})
@@ -30,7 +30,7 @@
"code": code,
"client_id": self.consumer_key,
"client_secret": self.get_password(fieldname="consumer_secret"),
- "redirect_uri": get_site_url(frappe.local.site) + "/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?",
+ "redirect_uri": "{0}/api/method/erpnext.crm.doctype.linkedin_settings.linkedin_settings.callback?".format(frappe.utils.get_url()),
}
headers = {
"Content-Type": "application/x-www-form-urlencoded"
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.json b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
index f92e7f0..36776e5 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.json
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.json
@@ -11,8 +11,8 @@
"consumer_key",
"column_break_5",
"consumer_secret",
- "oauth_token",
- "oauth_secret",
+ "access_token",
+ "access_token_secret",
"session_status"
],
"fields": [
@@ -42,20 +42,6 @@
"reqd": 1
},
{
- "fieldname": "oauth_token",
- "fieldtype": "Data",
- "hidden": 1,
- "label": "OAuth Token",
- "read_only": 1
- },
- {
- "fieldname": "oauth_secret",
- "fieldtype": "Password",
- "hidden": 1,
- "label": "OAuth Token Secret",
- "read_only": 1
- },
- {
"fieldname": "column_break_5",
"fieldtype": "Column Break"
},
@@ -72,12 +58,26 @@
"label": "Session Status",
"options": "Expired\nActive",
"read_only": 1
+ },
+ {
+ "fieldname": "access_token",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Access Token",
+ "read_only": 1
+ },
+ {
+ "fieldname": "access_token_secret",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Access Token Secret",
+ "read_only": 1
}
],
"image_field": "profile_pic",
"issingle": 1,
"links": [],
- "modified": "2020-04-21 22:06:43.726798",
+ "modified": "2020-05-13 17:50:47.934776",
"modified_by": "Administrator",
"module": "CRM",
"name": "Twitter Settings",
diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.py b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
index 7616b4c..976a23d 100644
--- a/erpnext/crm/doctype/twitter_settings/twitter_settings.py
+++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.py
@@ -31,13 +31,13 @@
try:
auth.get_access_token(oauth_verifier)
- api = self.get_api()
+ api = self.get_api(auth.access_token, auth.access_token_secret)
user = api.me()
profile_pic = (user._json["profile_image_url"]).replace("_normal","")
frappe.db.set_value(self.doctype, self.name, {
- "oauth_token" : auth.access_token,
- "oauth_secret" : auth.access_token_secret,
+ "access_token" : auth.access_token,
+ "access_token_secret" : auth.access_token_secret,
"account_name" : user._json["screen_name"],
"profile_pic" : profile_pic,
"session_status" : "Active"
@@ -49,11 +49,11 @@
frappe.msgprint(_("Error! Failed to get access token."))
frappe.throw(_('Invalid Consumer Key or Consumer Secret Key'))
- def get_api(self):
+ def get_api(self, access_token, access_token_secret):
# authentication of consumer key and secret
auth = tweepy.OAuthHandler(self.consumer_key, self.get_password(fieldname="consumer_secret"))
# authentication of access token and secret
- auth.set_access_token(self.oauth_token, self.get_password(fieldname="oauth_secret"))
+ auth.set_access_token(access_token, access_token_secret)
return tweepy.API(auth)
@@ -67,13 +67,13 @@
def upload_image(self, media):
media = get_file_path(media)
- api = self.get_api()
+ api = self.get_api(self.access_token, self.access_token_secret)
media = api.media_upload(media)
return media.media_id
def send_tweet(self, text, media_id=None):
- api = self.get_api()
+ api = self.get_api(self.access_token, self.access_token_secret)
try:
if media_id:
response = api.update_status(status = text, media_ids = [media_id])
diff --git a/erpnext/education/doctype/fees/fees.py b/erpnext/education/doctype/fees/fees.py
index f0d60fa..25d67d2 100644
--- a/erpnext/education/doctype/fees/fees.py
+++ b/erpnext/education/doctype/fees/fees.py
@@ -98,14 +98,16 @@
"debit_in_account_currency": self.grand_total,
"against_voucher": self.name,
"against_voucher_type": self.doctype
- })
+ }, item=self)
+
fee_gl_entry = self.get_gl_dict({
"account": self.income_account,
"against": self.student,
"credit": self.grand_total,
"credit_in_account_currency": self.grand_total,
"cost_center": self.cost_center
- })
+ }, item=self)
+
from erpnext.accounts.general_ledger import make_gl_entries
make_gl_entries([student_gl_entries, fee_gl_entry], cancel=(self.docstatus == 2),
update_outstanding="Yes", merge_entries=False)
diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
index b4a5bd1..a706223 100644
--- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
+++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py
@@ -209,7 +209,7 @@
result.append(new_transaction.name)
except Exception:
- frappe.throw(frappe.get_traceback())
+ frappe.throw(title=_('Bank transaction creation error'))
return result
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
index 87c22cc..eb7d4bd 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.js
@@ -43,7 +43,8 @@
return {
filters: {
'is_group': false,
- 'allow_appointments': true
+ 'allow_appointments': true,
+ 'company': frm.doc.company
}
};
});
@@ -158,11 +159,13 @@
age = __('{0} as on {1}', [age, data.message.age_as_on]);
}
}
+ frm.set_value('patient_name', data.message.patient_name);
frm.set_value('patient_age', age);
frm.set_value('patient_sex', data.message.sex);
}
});
} else {
+ frm.set_value('patient_name', '');
frm.set_value('patient_age', '');
frm.set_value('patient_sex', '');
}
@@ -177,15 +180,35 @@
name: frm.doc.appointment
},
callback: function(data) {
- frm.set_value('patient', data.message.patient);
- frm.set_value('procedure_template', data.message.procedure_template);
- frm.set_value('medical_department', data.message.department);
- frm.set_value('start_date', data.message.appointment_date);
- frm.set_value('start_time', data.message.appointment_time);
- frm.set_value('notes', data.message.notes);
- frm.set_value('service_unit', data.message.service_unit);
+ let values = {
+ 'patient':data.message.patient,
+ 'procedure_template': data.message.procedure_template,
+ 'medical_department': data.message.department,
+ 'practitioner': data.message.practitioner,
+ 'start_date': data.message.appointment_date,
+ 'start_time': data.message.appointment_time,
+ 'notes': data.message.notes,
+ 'service_unit': data.message.service_unit,
+ 'company': data.message.company
+ };
+ frm.set_value(values);
}
});
+ } else {
+ let values = {
+ 'patient': '',
+ 'patient_name': '',
+ 'patient_sex': '',
+ 'patient_age': '',
+ 'medical_department': '',
+ 'procedure_template': '',
+ 'start_date': '',
+ 'start_time': '',
+ 'notes': '',
+ 'service_unit': '',
+ 'inpatient_record': ''
+ };
+ frm.set_value(values);
}
},
@@ -234,9 +257,11 @@
name: frm.doc.practitioner
},
callback: function (data) {
- frappe.model.set_value(frm.doctype,frm.docname, 'medical_department',data.message.department);
+ frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', data.message.practitioner_name);
}
});
+ } else {
+ frappe.model.set_value(frm.doctype,frm.docname, 'practitioner_name', '');
}
},
@@ -284,14 +309,6 @@
});
-cur_frm.set_query('procedure_template', function(doc) {
- return {
- filters: {
- 'medical_department': doc.medical_department
- }
- };
-});
-
frappe.ui.form.on('Clinical Procedure Item', {
qty: function(frm, cdt, cdn) {
let d = locals[cdt][cdn];
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json
index 3c936bb..eaf8d80 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.json
@@ -7,28 +7,32 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "inpatient_record",
"naming_series",
- "procedure_template",
+ "title",
"appointment",
+ "procedure_template",
+ "column_break_30",
+ "company",
+ "invoiced",
+ "section_break_6",
"patient",
+ "patient_name",
"patient_sex",
"patient_age",
- "prescription",
- "medical_department",
- "practitioner",
+ "inpatient_record",
+ "notes",
"column_break_7",
"status",
+ "practitioner",
+ "practitioner_name",
+ "medical_department",
"service_unit",
- "warehouse",
"start_date",
"start_time",
"sample",
- "invoiced",
- "notes",
- "company",
"consumables_section",
"consume_stock",
+ "warehouse",
"items",
"section_break_24",
"invoice_separately_as_consumables",
@@ -36,6 +40,9 @@
"consumable_total_amount",
"column_break_27",
"consumption_details",
+ "sb_refs",
+ "column_break_34",
+ "prescription",
"amended_from"
],
"fields": [
@@ -56,15 +63,15 @@
{
"fieldname": "appointment",
"fieldtype": "Link",
- "in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Appointment",
- "options": "Patient Appointment"
+ "options": "Patient Appointment",
+ "set_only_once": 1
},
{
- "fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
- "in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Patient",
"options": "Patient",
"reqd": 1
@@ -88,17 +95,20 @@
"fieldtype": "Link",
"hidden": 1,
"label": "Procedure Prescription",
- "options": "Procedure Prescription"
+ "options": "Procedure Prescription",
+ "read_only": 1
},
{
"fieldname": "medical_department",
"fieldtype": "Link",
+ "in_standard_filter": 1,
"label": "Medical Department",
"options": "Medical Department"
},
{
"fieldname": "practitioner",
"fieldtype": "Link",
+ "in_standard_filter": 1,
"label": "Healthcare Practitioner",
"options": "Healthcare Practitioner"
},
@@ -208,6 +218,7 @@
"read_only": 1
},
{
+ "depends_on": "eval:!doc.__islocal",
"fieldname": "status",
"fieldtype": "Select",
"in_list_view": 1,
@@ -226,6 +237,8 @@
"read_only": 1
},
{
+ "collapsible": 1,
+ "collapsible_depends_on": "consume_stock",
"fieldname": "consumables_section",
"fieldtype": "Section Break",
"label": "Consumables"
@@ -237,11 +250,51 @@
{
"fieldname": "section_break_24",
"fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_30",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_6",
+ "fieldtype": "Section Break"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "sb_refs",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "patient_name",
+ "fieldtype": "Data",
+ "label": "Patient Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "practitioner_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Practitioner Name",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_34",
+ "fieldtype": "Column Break"
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-03-02 11:44:27.970651",
+ "modified": "2020-04-27 21:36:23.796924",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Clinical Procedure",
@@ -257,11 +310,27 @@
"report": 1,
"role": "Nursing User",
"share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Physician",
+ "share": 1,
+ "submit": 1,
"write": 1
}
],
"restrict_to_domain": "Healthcare",
"sort_field": "modified",
"sort_order": "DESC",
+ "title_field": "title",
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
index b7d7a62..e55a143 100644
--- a/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
+++ b/erpnext/healthcare/doctype/clinical_procedure/clinical_procedure.py
@@ -16,6 +16,7 @@
class ClinicalProcedure(Document):
def validate(self):
self.set_status()
+ self.set_title()
if self.consume_stock:
self.set_actual_qty()
@@ -37,7 +38,7 @@
template = frappe.get_doc('Clinical Procedure Template', self.procedure_template)
if template.sample:
patient = frappe.get_doc('Patient', self.patient)
- sample_collection = create_sample_doc(template, patient, None)
+ sample_collection = create_sample_doc(template, patient, None, self.company)
frappe.db.set_value('Clinical Procedure', self.name, 'sample', sample_collection.name)
self.reload()
@@ -50,6 +51,9 @@
elif self.docstatus == 2:
self.status = 'Cancelled'
+ def set_title(self):
+ self.title = _('{0} - {1}').format(self.patient_name or self.patient, self.procedure_template)[:100]
+
def complete_procedure(self):
if self.consume_stock and self.items:
stock_entry = make_stock_entry(self)
diff --git a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
index fd5b6e1..cb747f9 100644
--- a/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
+++ b/erpnext/healthcare/doctype/healthcare_practitioner/healthcare_practitioner.json
@@ -1,6 +1,5 @@
{
"actions": [],
- "allow_copy": 1,
"allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:",
@@ -51,17 +50,20 @@
"fieldname": "first_name",
"fieldtype": "Data",
"label": "First Name",
+ "no_copy": 1,
"reqd": 1
},
{
"fieldname": "middle_name",
"fieldtype": "Data",
- "label": "Middle Name (Optional)"
+ "label": "Middle Name (Optional)",
+ "no_copy": 1
},
{
"fieldname": "last_name",
"fieldtype": "Data",
- "label": "Last Name"
+ "label": "Last Name",
+ "no_copy": 1
},
{
"fieldname": "image",
@@ -226,6 +228,7 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Full Name",
+ "no_copy": 1,
"read_only": 1,
"search_index": 1
},
@@ -233,6 +236,7 @@
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
+ "no_copy": 1,
"options": "HLC-PRAC-.YYYY.-",
"report_hide": 1,
"set_only_once": 1
diff --git a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
index de08620..2f0115c 100644
--- a/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
+++ b/erpnext/healthcare/doctype/healthcare_settings/healthcare_settings.json
@@ -240,7 +240,7 @@
"label": "Patient Registration"
},
{
- "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.id}} . Please note this ID for future reference. \nThank You, Get well soon!",
+ "default": "Hello {{doc.patient}}, Thank you for registering with {{doc.company}}. Your ID is {{doc.name}} . Please note this ID for future reference. \nThank You!",
"depends_on": "send_registration_msg",
"fieldname": "registration_msg",
"fieldtype": "Small Text",
@@ -254,7 +254,7 @@
"label": "Appointment Confirmation"
},
{
- "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} by {{doc.start_dt}} at {{doc.company}}.\nThank you, Good day!",
+ "default": "Hello {{doc.patient}}, You have scheduled an appointment with {{doc.practitioner}} on {{doc.appointment_datetime}} at {{doc.company}}.\nThank you, Good day!",
"depends_on": "send_appointment_confirmation",
"fieldname": "appointment_confirmation_msg",
"fieldtype": "Small Text",
@@ -276,7 +276,7 @@
"label": "Appointment Reminder"
},
{
- "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_time}} at {{doc.company}}.\nThank you, Good day!\n",
+ "default": "Hello {{doc.patient}}, You have an appointment with {{doc.practitioner}} by {{doc.appointment_datetime}} at {{doc.company}}.\nThank you, Good day!\n",
"depends_on": "send_appointment_reminder",
"fieldname": "appointment_reminder_msg",
"fieldtype": "Small Text",
diff --git a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
index 92c11fb..c1b516d 100644
--- a/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
+++ b/erpnext/healthcare/doctype/inpatient_record/inpatient_record.json
@@ -1,980 +1,255 @@
{
- "allow_copy": 0,
- "allow_guest_to_view": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "naming_series:",
- "beta": 0,
- "creation": "2018-07-11 17:48:51.404139",
- "custom": 0,
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "",
- "editable_grid": 1,
- "engine": "InnoDB",
+ "actions": [],
+ "autoname": "naming_series:",
+ "creation": "2018-07-11 17:48:51.404139",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "section_break_1",
+ "naming_series",
+ "patient",
+ "patient_name",
+ "gender",
+ "blood_group",
+ "dob",
+ "mobile",
+ "email",
+ "phone",
+ "column_break_8",
+ "company",
+ "status",
+ "scheduled_date",
+ "admitted_datetime",
+ "expected_discharge",
+ "discharge_date",
+ "references",
+ "cb_admission",
+ "admission_practitioner",
+ "admission_encounter",
+ "cb_discharge",
+ "discharge_practitioner",
+ "discharge_encounter",
+ "sb_inpatient_occupancy",
+ "inpatient_occupancies",
+ "btn_transfer",
+ "sb_discharge_note",
+ "discharge_note"
+ ],
"fields": [
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "section_break_1",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "section_break_1",
+ "fieldtype": "Section Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "",
- "fieldname": "naming_series",
- "fieldtype": "Select",
- "hidden": 1,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Series",
- "length": 0,
- "no_copy": 0,
- "options": "HLC-INP-.YYYY.-",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "label": "Series",
+ "options": "HLC-INP-.YYYY.-"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "patient",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Patient",
- "length": 0,
- "no_copy": 0,
- "options": "Patient",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "patient",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Patient",
+ "options": "Patient",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "patient.patient_name",
- "fieldname": "patient_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Patient Name",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.patient_name",
+ "fieldname": "patient_name",
+ "fieldtype": "Data",
+ "label": "Patient Name",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "patient.sex",
- "fieldname": "gender",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Gender",
- "length": 0,
- "no_copy": 0,
- "options": "Gender",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.sex",
+ "fieldname": "gender",
+ "fieldtype": "Link",
+ "label": "Gender",
+ "options": "Gender",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "patient.blood_group",
- "fieldname": "blood_group",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Blood Group",
- "length": 0,
- "no_copy": 0,
- "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.blood_group",
+ "fieldname": "blood_group",
+ "fieldtype": "Select",
+ "label": "Blood Group",
+ "options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "dob",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Date of birth",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.dob",
+ "fieldname": "dob",
+ "fieldtype": "Date",
+ "label": "Date of birth",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "patient.mobile",
- "fieldname": "mobile",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Mobile",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.mobile",
+ "fieldname": "mobile",
+ "fieldtype": "Data",
+ "label": "Mobile",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "patient.email",
- "fieldname": "email",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Email",
- "length": 0,
- "no_copy": 0,
- "options": "Email",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.email",
+ "fieldname": "email",
+ "fieldtype": "Data",
+ "label": "Email",
+ "options": "Email",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fetch_from": "patient.phone",
- "fieldname": "phone",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Phone",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fetch_from": "patient.phone",
+ "fieldname": "phone",
+ "fieldtype": "Data",
+ "label": "Phone",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "column_break_8",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "column_break_8",
+ "fieldtype": "Column Break"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Status",
- "length": 0,
- "no_copy": 0,
- "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "in_list_view": 1,
+ "label": "Status",
+ "options": "Admission Scheduled\nAdmitted\nDischarge Scheduled\nDischarged",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Today",
- "fieldname": "scheduled_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Admission Schedule Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 1,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Today",
+ "fieldname": "scheduled_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Admission Schedule Date",
+ "reqd": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "default": "Today",
- "fieldname": "admitted_datetime",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Admitted Datetime",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "default": "Today",
+ "fieldname": "admitted_datetime",
+ "fieldtype": "Datetime",
+ "in_list_view": 1,
+ "label": "Admitted Datetime"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "expected_discharge",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Expected Discharge",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "expected_discharge",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Expected Discharge"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "discharge_date",
- "fieldtype": "Date",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 1,
- "in_standard_filter": 0,
- "label": "Discharge Date",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "discharge_date",
+ "fieldtype": "Date",
+ "in_list_view": 1,
+ "label": "Discharge Date"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 1,
- "columns": 0,
- "fieldname": "references",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "References",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "collapsible": 1,
+ "fieldname": "references",
+ "fieldtype": "Section Break",
+ "label": "References"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "cb_admission",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Admission",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "cb_admission",
+ "fieldtype": "Column Break",
+ "label": "Admission"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "admission_practitioner",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Healthcare Practitioner",
- "length": 0,
- "no_copy": 0,
- "options": "Healthcare Practitioner",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "admission_practitioner",
+ "fieldtype": "Link",
+ "label": "Healthcare Practitioner",
+ "options": "Healthcare Practitioner",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "admission_encounter",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Patient Encounter",
- "length": 0,
- "no_copy": 0,
- "options": "Patient Encounter",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "admission_encounter",
+ "fieldtype": "Link",
+ "label": "Patient Encounter",
+ "options": "Patient Encounter",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "cb_discharge",
- "fieldtype": "Column Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Discharge",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "cb_discharge",
+ "fieldtype": "Column Break",
+ "label": "Discharge"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "discharge_practitioner",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Healthcare Practitioner",
- "length": 0,
- "no_copy": 0,
- "options": "Healthcare Practitioner",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "discharge_practitioner",
+ "fieldtype": "Link",
+ "label": "Healthcare Practitioner",
+ "options": "Healthcare Practitioner",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "discharge_encounter",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Patient Encounter",
- "length": 0,
- "no_copy": 0,
- "options": "Patient Encounter",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "discharge_encounter",
+ "fieldtype": "Link",
+ "label": "Patient Encounter",
+ "options": "Patient Encounter",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "sb_inpatient_occupancy",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Inpatient Occupancy",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "sb_inpatient_occupancy",
+ "fieldtype": "Section Break",
+ "label": "Inpatient Occupancy"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "inpatient_occupancies",
- "fieldtype": "Table",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "length": 0,
- "no_copy": 0,
- "options": "Inpatient Occupancy",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "inpatient_occupancies",
+ "fieldtype": "Table",
+ "options": "Inpatient Occupancy",
+ "read_only": 1
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "btn_transfer",
- "fieldtype": "Button",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Transfer",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "fieldname": "btn_transfer",
+ "fieldtype": "Button",
+ "label": "Transfer"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "depends_on": "eval:doc.status != \"Admission Scheduled\"",
- "fieldname": "sb_discharge_note",
- "fieldtype": "Section Break",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "Discharge Note",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
- },
+ "depends_on": "eval:doc.status != \"Admission Scheduled\"",
+ "fieldname": "sb_discharge_note",
+ "fieldtype": "Section Break",
+ "label": "Discharge Note"
+ },
{
- "allow_bulk_edit": 0,
- "allow_in_quick_entry": 0,
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "columns": 0,
- "fieldname": "discharge_note",
- "fieldtype": "Text Editor",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_global_search": 0,
- "in_list_view": 0,
- "in_standard_filter": 0,
- "label": "",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "remember_last_selected_value": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
- "translatable": 0,
- "unique": 0
+ "fieldname": "discharge_note",
+ "fieldtype": "Text Editor"
+ },
+ {
+ "fetch_from": "admission_encounter.company",
+ "fieldname": "company",
+ "fieldtype": "Link",
+ "in_standard_filter": 1,
+ "label": "Company",
+ "options": "Company"
}
- ],
- "has_web_view": 0,
- "hide_heading": 0,
- "hide_toolbar": 0,
- "idx": 0,
- "image_view": 0,
- "in_create": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2018-08-21 14:44:43.168245",
- "modified_by": "Administrator",
- "module": "Healthcare",
- "name": "Inpatient Record",
- "name_case": "",
- "owner": "Administrator",
+ ],
+ "links": [],
+ "modified": "2020-04-07 13:13:39.351977",
+ "modified_by": "Administrator",
+ "module": "Healthcare",
+ "name": "Inpatient Record",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "cancel": 0,
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "Healthcare Administrator",
- "set_user_permissions": 0,
- "share": 1,
- "submit": 0,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Healthcare Administrator",
+ "share": 1,
"write": 1
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "restrict_to_domain": "Healthcare",
- "search_fields": "patient",
- "show_name_in_global_search": 0,
- "sort_field": "modified",
- "sort_order": "DESC",
- "title_field": "patient",
- "track_changes": 1,
- "track_seen": 0,
- "track_views": 0
-}
+ ],
+ "restrict_to_domain": "Healthcare",
+ "search_fields": "patient",
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "title_field": "patient",
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
index e15324c..4c2d3f6 100644
--- a/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
+++ b/erpnext/healthcare/doctype/inpatient_record/test_inpatient_record.py
@@ -8,7 +8,6 @@
from frappe.utils import now_datetime, today
from frappe.utils.make_random import get_random
from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
-from erpnext.healthcare.doctype.patient_appointment.test_patient_appointment import create_patient
class TestInpatientRecord(unittest.TestCase):
def test_admit_and_discharge(self):
@@ -112,3 +111,13 @@
service_unit_type.save(ignore_permissions = True)
return service_unit_type.name
return service_unit_type
+
+def create_patient():
+ patient = frappe.db.exists('Patient', '_Test IPD Patient')
+ if not patient:
+ patient = frappe.new_doc('Patient')
+ patient.first_name = '_Test IPD Patient'
+ patient.sex = 'Female'
+ patient.save(ignore_permissions=True)
+ patient = patient.name
+ return patient
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.js b/erpnext/healthcare/doctype/lab_test/lab_test.js
index 5b3f4c7..bf1ecc8 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.js
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.js
@@ -137,13 +137,13 @@
});
}
else{
- frappe.msgprint(__("Please select Patient to get Lab Tests"));
+ frappe.msgprint(__("Please select a Patient to get Lab Tests"));
}
};
var show_lab_tests = function(frm, result){
var d = new frappe.ui.Dialog({
- title: __("Lab Test Prescriptions"),
+ title: __("Lab Tests"),
fields: [
{
fieldtype: "HTML", fieldname: "lab_test"
@@ -161,7 +161,7 @@
<div class="col-xs-1">\
<a data-name="%(name)s" data-lab-test="%(lab_test)s"\
data-encounter="%(encounter)s" data-practitioner="%(practitioner)s"\
- data-invoiced="%(invoiced)s" href="#"><button class="btn btn-default btn-xs">Get Lab Test\
+ data-invoiced="%(invoiced)s" href="#"><button class="btn btn-default btn-xs">Get Lab Tests\
</button></a></div></div>', {name:y[0], lab_test: y[1], encounter:y[2], invoiced:y[3], practitioner:y[4], date:y[5]})).appendTo(html_field);
row.find("a").click(function() {
frm.doc.template = $(this).attr("data-lab-test");
@@ -180,9 +180,10 @@
return false;
});
});
- if(!result){
- var msg = "There are no Lab Test prescribed for "+frm.doc.patient;
- $(repl('<div class="col-xs-12" style="padding-top:20px;" >%(msg)s</div></div>', {msg: msg})).appendTo(html_field);
+ if(!result.length){
+ var msg = __("No Lab Tests found for the Patient {0}", [frm.doc.patient_name.bold()]);
+ html_field.empty();
+ $(repl('<div class="col-xs-12" style="padding-top:0px;" >%(msg)s</div>', {msg: msg})).appendTo(html_field);
}
d.show();
};
diff --git a/erpnext/healthcare/doctype/lab_test/lab_test.json b/erpnext/healthcare/doctype/lab_test/lab_test.json
index ccbc24b..17dc1ed 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.json
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.json
@@ -9,18 +9,18 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
- "inpatient_record",
"naming_series",
- "invoiced",
"patient",
"patient_name",
"patient_age",
"patient_sex",
- "practitioner",
+ "report_preference",
"email",
"mobile",
- "company",
+ "practitioner",
"c_b",
+ "inpatient_record",
+ "company",
"department",
"status",
"submitted_date",
@@ -31,7 +31,7 @@
"employee_name",
"employee_designation",
"user",
- "report_preference",
+ "invoiced",
"sb_first",
"lab_test_name",
"column_break_26",
@@ -153,7 +153,7 @@
{
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 1,
+ "in_standard_filter": 1,
"label": "Company",
"options": "Company",
"print_hide": 1,
@@ -168,6 +168,7 @@
"fieldname": "department",
"fieldtype": "Link",
"ignore_user_permissions": 1,
+ "in_standard_filter": 1,
"label": "Department",
"options": "Medical Department",
"search_index": 1
@@ -427,7 +428,7 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2020-03-23 19:37:06.617764",
+ "modified": "2020-04-04 19:16:29.131168",
"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 ea8ce25..b2c5e6b 100644
--- a/erpnext/healthcare/doctype/lab_test/lab_test.py
+++ b/erpnext/healthcare/doctype/lab_test/lab_test.py
@@ -69,9 +69,9 @@
lab_test_created = create_lab_test_from_encounter(docname)
if lab_test_created:
- frappe.msgprint(_("Lab Test(s) "+lab_test_created+" created."))
+ frappe.msgprint(_("Lab Test(s) {0} created".format(lab_test_created)))
else:
- frappe.msgprint(_("No Lab Test created"))
+ frappe.msgprint(_("No Lab Tests created"))
def create_lab_test_from_encounter(encounter_id):
lab_test_created = False
@@ -87,7 +87,7 @@
for lab_test_id in lab_test_ids:
template = get_lab_test_template(lab_test_id[1])
if template:
- lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template)
+ lab_test = create_lab_test_doc(lab_test_id[2], encounter.practitioner, patient, template, encounter.company)
lab_test.save(ignore_permissions = True)
frappe.db.set_value("Lab Prescription", lab_test_id[0], "lab_test_created", 1)
if not lab_test_created:
@@ -111,7 +111,7 @@
if lab_test_created != 1:
template = get_lab_test_template(item.item_code)
if template:
- lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template)
+ lab_test = create_lab_test_doc(True, invoice.ref_practitioner, patient, template, invoice.company)
if item.reference_dt == "Lab Prescription":
lab_test.prescription = item.reference_dn
lab_test.save(ignore_permissions = True)
@@ -121,7 +121,7 @@
if not lab_tests_created:
lab_tests_created = lab_test.name
else:
- lab_tests_created += ", "+lab_test.name
+ lab_tests_created += ", " + lab_test.name
return lab_tests_created
def get_lab_test_template(item):
@@ -141,7 +141,7 @@
return template_exists
return False
-def create_lab_test_doc(invoiced, practitioner, patient, template):
+def create_lab_test_doc(invoiced, practitioner, patient, template, company):
lab_test = frappe.new_doc("Lab Test")
lab_test.invoiced = invoiced
lab_test.practitioner = practitioner
@@ -150,11 +150,12 @@
lab_test.patient_sex = patient.sex
lab_test.email = patient.email
lab_test.mobile = patient.mobile
+ lab_test.report_preference = patient.report_preference
lab_test.department = template.department
lab_test.template = template.name
lab_test.lab_test_group = template.lab_test_group
lab_test.result_date = getdate()
- lab_test.report_preference = patient.report_preference
+ lab_test.company = company
return lab_test
def create_normals(template, lab_test):
@@ -190,7 +191,7 @@
special.require_result_value = 1
special.template = template.name
-def create_sample_doc(template, patient, invoice):
+def create_sample_doc(template, patient, invoice, company = None):
if template.sample:
sample_exists = frappe.db.exists({
"doctype": "Sample Collection",
@@ -221,6 +222,8 @@
sample_collection.sample = template.sample
sample_collection.sample_uom = template.sample_uom
sample_collection.sample_qty = template.sample_qty
+ 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.save(ignore_permissions=True)
@@ -229,7 +232,7 @@
def create_sample_collection(lab_test, template, patient, invoice):
if(frappe.db.get_value("Healthcare Settings", None, "create_sample_collection_for_lab_test") == "1"):
- sample_collection = create_sample_doc(template, patient, invoice)
+ sample_collection = create_sample_doc(template, patient, invoice, lab_test.company)
if(sample_collection):
lab_test.sample = sample_collection.name
return lab_test
diff --git a/erpnext/healthcare/doctype/patient/patient.js b/erpnext/healthcare/doctype/patient/patient.js
index d5df956..490f247 100644
--- a/erpnext/healthcare/doctype/patient/patient.js
+++ b/erpnext/healthcare/doctype/patient/patient.js
@@ -10,6 +10,8 @@
]
};
});
+ frm.set_query('customer_group', {'is_group': 0});
+ frm.set_query('default_price_list', { 'selling': 1});
if (frappe.defaults.get_default('patient_name_by') != 'Naming Series') {
frm.toggle_display('naming_series', false);
@@ -40,6 +42,7 @@
frm.add_custom_button(__('Patient Encounter'), function () {
create_encounter(frm);
}, 'Create');
+ frm.toggle_enable(['customer'], 0); // ToDo, allow change only if no transactions booked or better, add merge option
}
},
onload: function (frm) {
diff --git a/erpnext/healthcare/doctype/patient/patient.json b/erpnext/healthcare/doctype/patient/patient.json
index 4258e40..8af1a9c 100644
--- a/erpnext/healthcare/doctype/patient/patient.json
+++ b/erpnext/healthcare/doctype/patient/patient.json
@@ -24,13 +24,20 @@
"image",
"column_break_14",
"status",
- "inpatient_status",
"inpatient_record",
- "customer",
+ "inpatient_status",
+ "report_preference",
"mobile",
"email",
"phone",
- "report_preference",
+ "customer_details_section",
+ "customer",
+ "customer_group",
+ "territory",
+ "column_break_24",
+ "default_currency",
+ "default_price_list",
+ "language",
"personal_and_social_history",
"occupation",
"column_break_25",
@@ -52,9 +59,7 @@
"surrounding_factors",
"other_risk_factors",
"more_info",
- "patient_details",
- "ac_sb",
- "default_currency"
+ "patient_details"
],
"fields": [
{
@@ -67,6 +72,7 @@
{
"fieldname": "inpatient_status",
"fieldtype": "Select",
+ "in_preview": 1,
"label": "Inpatient Status",
"options": "\nAdmission Scheduled\nAdmitted\nDischarge Scheduled",
"read_only": 1
@@ -101,6 +107,7 @@
{
"fieldname": "sex",
"fieldtype": "Link",
+ "in_preview": 1,
"label": "Gender",
"options": "Gender",
"reqd": 1
@@ -109,6 +116,7 @@
"bold": 1,
"fieldname": "blood_group",
"fieldtype": "Select",
+ "in_preview": 1,
"label": "Blood Group",
"options": "\nA Positive\nA Negative\nAB Positive\nAB Negative\nB Positive\nB Negative\nO Positive\nO Negative"
},
@@ -116,6 +124,7 @@
"bold": 1,
"fieldname": "dob",
"fieldtype": "Date",
+ "in_preview": 1,
"label": "Date of birth"
},
{
@@ -142,6 +151,7 @@
"fieldname": "image",
"fieldtype": "Attach Image",
"hidden": 1,
+ "in_preview": 1,
"label": "Image",
"no_copy": 1,
"print_hide": 1,
@@ -157,7 +167,8 @@
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Customer",
- "options": "Customer"
+ "options": "Customer",
+ "set_only_once": 1
},
{
"fieldname": "report_preference",
@@ -171,7 +182,8 @@
"fieldtype": "Data",
"in_list_view": 1,
"in_standard_filter": 1,
- "label": "Mobile"
+ "label": "Mobile",
+ "options": "Phone"
},
{
"bold": 1,
@@ -186,7 +198,8 @@
"fieldname": "phone",
"fieldtype": "Data",
"in_filter": 1,
- "label": "Phone"
+ "label": "Phone",
+ "options": "Phone"
},
{
"collapsible": 1,
@@ -268,25 +281,25 @@
"fieldname": "tobacco_past_use",
"fieldtype": "Data",
"ignore_xss_filter": 1,
- "label": "Tobacco Consumption Habbits (Past)"
+ "label": "Tobacco Consumption (Past)"
},
{
"fieldname": "tobacco_current_use",
"fieldtype": "Data",
"ignore_xss_filter": 1,
- "label": "Tobacco Consumption Habbits (Present)"
+ "label": "Tobacco Consumption (Present)"
},
{
"fieldname": "alcohol_past_use",
"fieldtype": "Data",
"ignore_xss_filter": 1,
- "label": "Alcohol Consumption Habbits (Past)"
+ "label": "Alcohol Consumption (Past)"
},
{
"fieldname": "alcohol_current_use",
"fieldtype": "Data",
"ignore_user_permissions": 1,
- "label": "Alcohol Consumption Habbits (Present)"
+ "label": "Alcohol Consumption (Present)"
},
{
"fieldname": "column_break_32",
@@ -321,19 +334,10 @@
"label": "Patient Details"
},
{
- "collapsible": 1,
- "fieldname": "ac_sb",
- "fieldtype": "Section Break",
- "label": "Account Details"
- },
- {
"fieldname": "default_currency",
"fieldtype": "Link",
- "hidden": 1,
- "ignore_xss_filter": 1,
- "label": "Default Currency",
- "options": "Currency",
- "print_hide": 1
+ "label": "Billing Currency",
+ "options": "Currency"
},
{
"fieldname": "last_name",
@@ -351,13 +355,47 @@
"fieldname": "middle_name",
"fieldtype": "Data",
"label": "Middle Name (optional)"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "customer_details_section",
+ "fieldtype": "Section Break",
+ "label": "Customer Details"
+ },
+ {
+ "fieldname": "customer_group",
+ "fieldtype": "Link",
+ "label": "Customer Group",
+ "options": "Customer Group"
+ },
+ {
+ "fieldname": "territory",
+ "fieldtype": "Link",
+ "label": "Territory",
+ "options": "Territory"
+ },
+ {
+ "fieldname": "column_break_24",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "default_price_list",
+ "fieldtype": "Link",
+ "label": "Default Price List",
+ "options": "Price List"
+ },
+ {
+ "fieldname": "language",
+ "fieldtype": "Link",
+ "label": "Print Language",
+ "options": "Language"
}
],
"icon": "fa fa-user",
"image_field": "image",
"links": [],
"max_attachments": 50,
- "modified": "2020-04-06 12:55:30.807744",
+ "modified": "2020-04-25 17:24:32.146415",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient",
diff --git a/erpnext/healthcare/doctype/patient/patient.py b/erpnext/healthcare/doctype/patient/patient.py
index e304a0b..30a1e45 100644
--- a/erpnext/healthcare/doctype/patient/patient.py
+++ b/erpnext/healthcare/doctype/patient/patient.py
@@ -10,6 +10,7 @@
import dateutil
from frappe.model.naming import set_name_by_naming_series
from frappe.utils.nestedset import get_root_of
+from erpnext import get_default_currency
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account, get_income_account, send_registration_sms
class Patient(Document):
@@ -17,6 +18,9 @@
self.set_full_name()
self.add_as_website_user()
+ def before_insert(self):
+ self.set_missing_customer_details()
+
def after_insert(self):
self.add_as_website_user()
self.reload()
@@ -26,6 +30,25 @@
frappe.db.set_value('Patient', self.name, 'status', 'Disabled')
else:
send_registration_sms(self)
+ self.reload() # self.notify_update()
+
+ def on_update(self):
+ if self.customer:
+ customer = frappe.get_doc('Customer', self.customer)
+ if self.customer_group:
+ customer.customer_group = self.customer_group
+ if self.territory:
+ customer.territory = self.territory
+
+ customer.customer_name = self.patient_name
+ customer.default_price_list = self.default_price_list
+ customer.default_currency = self.default_currency
+ customer.language = self.language
+ customer.ignore_mandatory = True
+ customer.save(ignore_permissions=True)
+ else:
+ if frappe.db.get_single_value('Healthcare Settings', 'link_customer_to_patient'):
+ create_customer(self)
def set_full_name(self):
if self.last_name:
@@ -33,6 +56,22 @@
else:
self.patient_name = self.first_name
+ def set_missing_customer_details(self):
+ if not self.customer_group:
+ self.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group') or get_root_of('Customer Group')
+ if not self.territory:
+ self.territory = frappe.db.get_single_value('Selling Settings', 'territory') or get_root_of('Territory')
+ if not self.default_price_list:
+ self.default_price_list = frappe.db.get_single_value('Selling Settings', 'selling_price_list')
+
+ if not self.customer_group or not self.territory or not self.default_price_list:
+ frappe.msgprint(_('Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings'), alert=True)
+
+ if not self.default_currency:
+ self.default_currency = get_default_currency()
+ if not self.language:
+ self.language = frappe.db.get_single_value('System Settings', 'language')
+
def add_as_website_user(self):
if self.email:
if not frappe.db.exists ('User', self.email):
@@ -86,19 +125,15 @@
return {'invoice': sales_invoice.name}
def create_customer(doc):
- customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
- territory = frappe.db.get_single_value('Selling Settings', 'territory')
- if not (customer_group and territory):
- customer_group = get_root_of('Customer Group')
- territory = get_root_of('Territory')
- frappe.msgprint(_('Please set default customer group and territory in Selling Settings'), alert=True)
-
customer = frappe.get_doc({
'doctype': 'Customer',
'customer_name': doc.patient_name,
- 'customer_group': customer_group,
- 'territory' : territory,
- 'customer_type': 'Individual'
+ 'customer_group': doc.customer_group or frappe.db.get_single_value('Selling Settings', 'customer_group'),
+ 'territory' : doc.territory or frappe.db.get_single_value('Selling Settings', 'territory'),
+ 'customer_type': 'Individual',
+ 'default_currency': doc.default_currency,
+ 'default_price_list': doc.default_price_list,
+ 'language': doc.language
}).insert(ignore_permissions=True, ignore_mandatory=True)
frappe.db.set_value('Patient', doc.name, 'customer', customer.name)
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
index fa58934..f7ed31b 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.js
@@ -32,8 +32,9 @@
frm.set_query('service_unit', function(){
return {
filters: {
- 'is_group': 0,
- 'allow_appointments': 1
+ 'is_group': false,
+ 'allow_appointments': true,
+ 'company': frm.doc.company
}
};
});
@@ -127,6 +128,11 @@
patient: function(frm) {
if (frm.doc.patient) {
frm.trigger('toggle_payment_fields');
+ } else {
+ frm.set_value('patient_name', '');
+ frm.set_value('patient_sex', '');
+ frm.set_value('patient_age', '');
+ frm.set_value('inpatient_record', '');
}
},
@@ -230,7 +236,6 @@
d.hide();
frm.enable_save();
frm.save();
- frm.enable_save();
d.get_primary_btn().attr('disabled', true);
}
});
@@ -481,6 +486,7 @@
frappe.route_options = {
'patient': frm.doc.patient,
'appointment': frm.doc.name,
+ 'company': frm.doc.company
};
frappe.new_doc('Vital Signs');
};
@@ -513,6 +519,7 @@
callback: function (data) {
frappe.model.set_value(frm.doctype, frm.docname, 'department', data.message.department);
frappe.model.set_value(frm.doctype, frm.docname, 'paid_amount', data.message.op_consulting_charge);
+ frappe.model.set_value(frm.doctype, frm.docname, 'billing_item', data.message.op_consulting_charge_item);
}
});
}
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
index 57e6c47..b8a400c 100644
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.json
@@ -10,40 +10,44 @@
"engine": "InnoDB",
"field_order": [
"naming_series",
+ "title",
+ "status",
"patient",
"patient_name",
"patient_sex",
"patient_age",
"inpatient_record",
"column_break_1",
- "status",
+ "company",
+ "service_unit",
"procedure_template",
"get_procedure_from_encounter",
"procedure_prescription",
"therapy_type",
"get_prescribed_therapies",
"therapy_plan",
- "service_unit",
- "section_break_12",
"practitioner",
+ "practitioner_name",
"department",
+ "section_break_12",
"appointment_type",
+ "duration",
"column_break_17",
"appointment_date",
"appointment_time",
"appointment_datetime",
- "duration",
"section_break_16",
"mode_of_payment",
- "paid_amount",
- "company",
+ "billing_item",
"column_break_2",
+ "paid_amount",
"invoiced",
"ref_sales_invoice",
"section_break_3",
- "notes",
"referring_practitioner",
- "reminded"
+ "reminded",
+ "column_break_36",
+ "notes"
],
"fields": [
{
@@ -55,7 +59,6 @@
"read_only": 1
},
{
- "fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
"ignore_user_permissions": 1,
@@ -79,7 +82,8 @@
"fieldname": "duration",
"fieldtype": "Int",
"in_filter": 1,
- "label": "Duration (In Minutes)"
+ "label": "Duration (In Minutes)",
+ "set_only_once": 1
},
{
"fieldname": "column_break_1",
@@ -98,6 +102,7 @@
"search_index": 1
},
{
+ "depends_on": "eval:doc.patient;",
"fieldname": "procedure_template",
"fieldtype": "Link",
"label": "Clinical Procedure Template",
@@ -117,7 +122,8 @@
"label": "Procedure Prescription",
"no_copy": 1,
"options": "Procedure Prescription",
- "print_hide": 1
+ "print_hide": 1,
+ "read_only": 1
},
{
"fieldname": "service_unit",
@@ -128,7 +134,8 @@
},
{
"fieldname": "section_break_12",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Appointment Details"
},
{
"fieldname": "practitioner",
@@ -143,6 +150,7 @@
"set_only_once": 1
},
{
+ "fetch_from": "practitioner.department",
"fieldname": "department",
"fieldtype": "Link",
"ignore_user_permissions": 1,
@@ -173,11 +181,13 @@
"fieldtype": "Time",
"in_list_view": 1,
"label": "Time",
- "read_only": 1
+ "read_only": 1,
+ "reqd": 1
},
{
"fieldname": "section_break_16",
- "fieldtype": "Section Break"
+ "fieldtype": "Section Break",
+ "label": "Payments"
},
{
"fetch_from": "patient.patient_name",
@@ -206,6 +216,7 @@
{
"fieldname": "appointment_datetime",
"fieldtype": "Datetime",
+ "hidden": 1,
"label": "Appointment Datetime",
"print_hide": 1,
"read_only": 1,
@@ -237,12 +248,12 @@
{
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 1,
+ "in_standard_filter": 1,
"label": "Company",
"no_copy": 1,
"options": "Company",
- "print_hide": 1,
- "report_hide": 1
+ "reqd": 1,
+ "set_only_once": 1
},
{
"collapsible": 1,
@@ -307,10 +318,37 @@
"label": "Series",
"options": "HLC-APP-.YYYY.-",
"set_only_once": 1
+ },
+ {
+ "fieldname": "billing_item",
+ "fieldtype": "Link",
+ "label": "Billing Item",
+ "options": "Item",
+ "read_only": 1
+ },
+ {
+ "fieldname": "column_break_36",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fetch_from": "practitioner.practitioner_name",
+ "fieldname": "practitioner_name",
+ "fieldtype": "Data",
+ "label": "Practitioner Name",
+ "read_only": 1
}
],
"links": [],
- "modified": "2020-03-31 16:16:32.116865",
+ "modified": "2020-04-27 21:36:06.404062",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Appointment",
@@ -358,7 +396,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "patient",
+ "title_field": "title",
"track_changes": 1,
"track_seen": 1
}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
index c4ec30f..9eb6e77 100755
--- a/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/patient_appointment.py
@@ -21,6 +21,7 @@
self.set_appointment_datetime()
self.validate_customer_created()
self.set_status()
+ self.set_title()
def after_insert(self):
self.update_prescription_details()
@@ -28,6 +29,10 @@
self.update_fee_validity()
send_confirmation_msg(self)
+ def set_title(self):
+ self.title = _('{0} with {1}').format(self.patient_name or self.patient,
+ self.practitioner_name or self.practitioner)
+
def set_status(self):
today = getdate()
appointment_date = getdate(self.appointment_date)
@@ -119,25 +124,28 @@
if automate_invoicing and not appointment_invoiced and not fee_validity:
sales_invoice = frappe.new_doc('Sales Invoice')
+ sales_invoice.patient = appointment_doc.patient
sales_invoice.customer = frappe.get_value('Patient', appointment_doc.patient, 'customer')
sales_invoice.appointment = appointment_doc.name
sales_invoice.due_date = getdate()
- sales_invoice.is_pos = 1
sales_invoice.company = appointment_doc.company
sales_invoice.debit_to = get_receivable_account(appointment_doc.company)
item = sales_invoice.append('items', {})
item = get_appointment_item(appointment_doc, item)
- payment = sales_invoice.append('payments', {})
- payment.mode_of_payment = appointment_doc.mode_of_payment
- payment.amount = appointment_doc.paid_amount
+ # Add payments if payment details are supplied else proceed to create invoice as Unpaid
+ if appointment_doc.mode_of_payment and appointment_doc.paid_amount:
+ sales_invoice.is_pos = 1
+ payment = sales_invoice.append('payments', {})
+ payment.mode_of_payment = appointment_doc.mode_of_payment
+ payment.amount = appointment_doc.paid_amount
sales_invoice.set_missing_values(for_validate=True)
sales_invoice.flags.ignore_mandatory = True
sales_invoice.save(ignore_permissions=True)
sales_invoice.submit()
- frappe.msgprint(_('Sales Invoice {0} created as paid'.format(sales_invoice.name)), alert=True)
+ frappe.msgprint(_('Sales Invoice {0} created'.format(sales_invoice.name)), alert=True)
frappe.db.set_value('Patient Appointment', appointment_doc.name, 'invoiced', 1)
frappe.db.set_value('Patient Appointment', appointment_doc.name, 'ref_sales_invoice', sales_invoice.name)
@@ -343,8 +351,8 @@
['practitioner', 'practitioner'],
['medical_department', 'department'],
['patient_sex', 'patient_sex'],
- ['encounter_date', 'appointment_date'],
- ['invoiced', 'invoiced']
+ ['invoiced', 'invoiced'],
+ ['company', 'company']
]
}
}, target_doc)
@@ -370,17 +378,19 @@
frappe.db.set_value('Patient Appointment', doc.name, 'reminded', 1)
def send_message(doc, message):
- patient = frappe.get_doc('Patient', doc.patient)
- if patient.mobile:
+ patient_mobile = frappe.db.get_value('Patient', doc.patient, 'mobile')
+ if patient_mobile:
context = {'doc': doc, 'alert': doc, 'comments': None}
if doc.get('_comments'):
context['comments'] = json.loads(doc.get('_comments'))
# jinja to string convertion happens here
message = frappe.render_template(message, context)
- number = [patient.mobile]
- send_sms(number, message)
-
+ number = [patient_mobile]
+ try:
+ send_sms(number, message)
+ except Exception as e:
+ frappe.msgprint(_('SMS not sent, please check SMS Settings'), alert=True)
@frappe.whitelist()
def get_events(start, end, filters=None):
diff --git a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
index 7075af5..eeed157 100644
--- a/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
+++ b/erpnext/healthcare/doctype/patient_appointment/test_patient_appointment.py
@@ -4,14 +4,15 @@
from __future__ import unicode_literals
import unittest
import frappe
-from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status
+from erpnext.healthcare.doctype.patient_appointment.patient_appointment import update_status, make_encounter
from frappe.utils import nowdate, add_days
from frappe.utils.make_random import get_random
class TestPatientAppointment(unittest.TestCase):
def setUp(self):
frappe.db.sql("""delete from `tabPatient Appointment`""")
- frappe.db.sql("""delete from `tabFee Validity""")
+ frappe.db.sql("""delete from `tabFee Validity`""")
+ frappe.db.sql("""delete from `tabPatient Encounter`""")
def test_status(self):
patient, medical_department, practitioner = create_healthcare_docs()
@@ -23,6 +24,19 @@
create_encounter(appointment)
self.assertEquals(frappe.db.get_value('Patient Appointment', appointment.name, 'status'), 'Closed')
+ def test_start_encounter(self):
+ patient, medical_department, practitioner = create_healthcare_docs()
+ frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
+ appointment = create_appointment(patient, practitioner, add_days(nowdate(), 4), invoice = 1)
+ self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
+ encounter = make_encounter(appointment.name)
+ self.assertTrue(encounter)
+ self.assertEqual(encounter.company, appointment.company)
+ self.assertEqual(encounter.practitioner, appointment.practitioner)
+ self.assertEqual(encounter.patient, appointment.patient)
+ # invoiced flag mapped from appointment
+ self.assertEqual(encounter.invoiced, frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'))
+
def test_invoicing(self):
patient, medical_department, practitioner = create_healthcare_docs()
frappe.db.set_value('Healthcare Settings', None, 'enable_free_follow_ups', 0)
@@ -33,7 +47,11 @@
frappe.db.set_value('Healthcare Settings', None, 'automate_appointment_invoicing', 1)
appointment = create_appointment(patient, practitioner, add_days(nowdate(), 2), invoice=1)
self.assertEqual(frappe.db.get_value('Patient Appointment', appointment.name, 'invoiced'), 1)
- self.assertTrue(frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice'))
+ sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+ self.assertTrue(sales_invoice_name)
+ self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'company'), appointment.company)
+ self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'patient'), appointment.patient)
+ self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'paid_amount'), appointment.paid_amount)
def test_appointment_cancel(self):
patient, medical_department, practitioner = create_healthcare_docs()
@@ -53,8 +71,8 @@
appointment = create_appointment(patient, practitioner, nowdate(), invoice=1)
update_status(appointment.name, 'Cancelled')
# check invoice cancelled
- sales_invoice = frappe.db.get_value('Patient Appointment', appointment.name, 'ref_sales_invoice')
- self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice, 'status'), 'Cancelled')
+ sales_invoice_name = frappe.db.get_value('Sales Invoice Item', {'reference_dn': appointment.name}, 'parent')
+ self.assertEqual(frappe.db.get_value('Sales Invoice', sales_invoice_name, 'status'), 'Cancelled')
def create_healthcare_docs():
@@ -90,14 +108,15 @@
patient = patient.name
return patient
-def create_encounter(appointment=None):
- encounter = frappe.new_doc('Patient Encounter')
+def create_encounter(appointment):
if appointment:
+ encounter = frappe.new_doc('Patient Encounter')
encounter.appointment = appointment.name
encounter.patient = appointment.patient
encounter.practitioner = appointment.practitioner
encounter.encounter_date = appointment.appointment_date
encounter.encounter_time = appointment.appointment_time
+ encounter.company = appointment.company
encounter.save()
encounter.submit()
return encounter
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
index 78e789d..2410f8e 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.js
@@ -25,15 +25,16 @@
refresh_field('lab_test_prescription');
if (!frm.doc.__islocal) {
-
- if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') {
- frm.add_custom_button(__('Schedule Discharge'), function() {
- schedule_discharge(frm);
- });
- } else if (frm.doc.inpatient_status != 'Discharge Scheduled') {
- frm.add_custom_button(__('Schedule Admission'), function() {
- schedule_inpatient(frm);
- });
+ if (frm.doc.docstatus === 1) {
+ if (frm.doc.inpatient_status == 'Admission Scheduled' || frm.doc.inpatient_status == 'Admitted') {
+ frm.add_custom_button(__('Schedule Discharge'), function() {
+ schedule_discharge(frm);
+ });
+ } else if (frm.doc.inpatient_status != 'Discharge Scheduled') {
+ frm.add_custom_button(__('Schedule Admission'), function() {
+ schedule_inpatient(frm);
+ });
+ }
}
frm.add_custom_button(__('Patient History'), function() {
@@ -101,6 +102,11 @@
frm.events.set_patient_info(frm);
},
+ practitioner: function(frm) {
+ if (!frm.doc.practitioner) {
+ frm.set_value('practitioner_name', '');
+ }
+ },
set_appointment_fields: function(frm) {
if (frm.doc.appointment) {
frappe.call({
@@ -114,9 +120,11 @@
'patient':data.message.patient,
'type': data.message.appointment_type,
'practitioner': data.message.practitioner,
- 'invoiced': data.message.invoiced
+ 'invoiced': data.message.invoiced,
+ 'company': data.message.company
};
frm.set_value(values);
+ frm.set_df_property('patient', 'read_only', 1);
}
});
}
@@ -133,6 +141,7 @@
'inpatient_status': ''
};
frm.set_value(values);
+ frm.set_df_property('patient', 'read_only', 0);
}
},
@@ -148,19 +157,25 @@
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);
- if (data.message.inpatient_record) {
- frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', data.message.inpatient_record);
- frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', data.message.inpatient_status);
- }
+ let values = {
+ 'patient_age': age,
+ 'patient_name':data.message.patient_name,
+ 'patient_sex': data.message.sex,
+ 'inpatient_record': data.message.inpatient_record,
+ 'inpatient_status': data.message.inpatient_status
+ };
+ frm.set_value(values);
}
});
} else {
- frappe.model.set_value(frm.doctype, frm.docname, 'patient_sex', '');
- frappe.model.set_value(frm.doctype, frm.docname, 'patient_age', '');
- frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_record', '');
- frappe.model.set_value(frm.doctype, frm.docname, 'inpatient_status', '');
+ let values = {
+ 'patient_age': '',
+ 'patient_name':'',
+ 'patient_sex': '',
+ 'inpatient_record': '',
+ 'inpatient_status': ''
+ };
+ frm.set_value(values);
}
}
});
@@ -212,8 +227,8 @@
}
frappe.route_options = {
'patient': frm.doc.patient,
- 'appointment': frm.doc.appointment,
- 'encounter': frm.doc.name
+ 'encounter': frm.doc.name,
+ 'company': frm.doc.company
};
frappe.new_doc('Vital Signs');
};
@@ -224,7 +239,8 @@
}
frappe.route_options = {
'patient': frm.doc.patient,
- 'medical_department': frm.doc.medical_department
+ 'medical_department': frm.doc.medical_department,
+ 'company': frm.doc.company
};
frappe.new_doc('Clinical Procedure');
};
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
index 5f11039..05eec87 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.json
@@ -11,23 +11,23 @@
"engine": "InnoDB",
"field_order": [
"naming_series",
+ "title",
"appointment",
"appointment_type",
"patient",
"patient_name",
"patient_sex",
"patient_age",
- "company",
+ "inpatient_record",
+ "inpatient_status",
"column_break_6",
- "practitioner",
- "medical_department",
+ "company",
"encounter_date",
"encounter_time",
+ "practitioner",
+ "practitioner_name",
+ "medical_department",
"invoiced",
- "section_break_1",
- "inpatient_record",
- "column_break_17",
- "inpatient_status",
"sb_symptoms",
"symptoms",
"symptoms_in_print",
@@ -47,6 +47,7 @@
"therapies",
"section_break_33",
"encounter_comment",
+ "sb_refs",
"amended_from"
],
"fields": [
@@ -58,12 +59,6 @@
"read_only": 1
},
{
- "collapsible": 1,
- "fieldname": "section_break_1",
- "fieldtype": "Section Break",
- "label": "Inpatient Details"
- },
- {
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
@@ -77,14 +72,13 @@
"ignore_user_permissions": 1,
"label": "Appointment",
"options": "Patient Appointment",
- "search_index": 1
+ "search_index": 1,
+ "set_only_once": 1
},
{
- "fetch_from": "inpatient_record.patient",
"fieldname": "patient",
"fieldtype": "Link",
"ignore_user_permissions": 1,
- "in_list_view": 1,
"in_standard_filter": 1,
"label": "Patient",
"options": "Patient",
@@ -92,7 +86,6 @@
"search_index": 1
},
{
- "fetch_from": "patient.patient_name",
"fieldname": "patient_name",
"fieldtype": "Data",
"label": "Patient Name",
@@ -114,7 +107,6 @@
{
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 1,
"label": "Company",
"options": "Company"
},
@@ -125,7 +117,6 @@
{
"fieldname": "practitioner",
"fieldtype": "Link",
- "in_list_view": 1,
"in_standard_filter": 1,
"label": "Healthcare Practitioner",
"options": "Healthcare Practitioner",
@@ -207,29 +198,29 @@
{
"fieldname": "codification_table",
"fieldtype": "Table",
- "label": "Medical Coding",
+ "label": "Medical Codes",
"options": "Codification Table"
},
{
"fieldname": "sb_drug_prescription",
"fieldtype": "Section Break",
- "label": "Medication"
+ "label": "Medications"
},
{
"fieldname": "drug_prescription",
"fieldtype": "Table",
- "label": "Drug Prescription",
+ "label": "Items",
"options": "Drug Prescription"
},
{
"fieldname": "sb_test_prescription",
"fieldtype": "Section Break",
- "label": "Investigation"
+ "label": "Investigations"
},
{
"fieldname": "lab_test_prescription",
"fieldtype": "Table",
- "label": "Lab Prescription",
+ "label": "Lab Tests",
"options": "Lab Prescription"
},
{
@@ -240,7 +231,7 @@
{
"fieldname": "procedure_prescription",
"fieldtype": "Table",
- "label": "Procedure Prescription",
+ "label": "Clinical Procedures",
"no_copy": 1,
"options": "Procedure Prescription"
},
@@ -299,7 +290,6 @@
"fieldname": "medical_department",
"fieldtype": "Link",
"ignore_user_permissions": 1,
- "in_list_view": 1,
"in_standard_filter": 1,
"label": "Department",
"options": "Medical Department",
@@ -312,13 +302,31 @@
"read_only": 1
},
{
- "fieldname": "column_break_17",
- "fieldtype": "Column Break"
+ "fieldname": "sb_refs",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fetch_from": "practitioner.practitioner_name",
+ "fieldname": "practitioner_name",
+ "fieldtype": "Data",
+ "in_list_view": 1,
+ "label": "Practitioner Name",
+ "read_only": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-04-14 16:18:08.180457",
+ "modified": "2020-04-27 21:58:29.789797",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Patient Encounter",
@@ -345,7 +353,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "patient",
+ "title_field": "title",
"track_changes": 1,
"track_seen": 1
}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
index 1734c28..56401a3 100644
--- a/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
+++ b/erpnext/healthcare/doctype/patient_encounter/patient_encounter.py
@@ -10,6 +10,9 @@
from frappe import _
class PatientEncounter(Document):
+ def validate(self):
+ self.set_title()
+
def on_update(self):
if self.appointment:
frappe.db.set_value('Patient Appointment', self.appointment, 'status', 'Closed')
@@ -29,6 +32,10 @@
def on_submit(self):
create_therapy_plan(self)
+ def set_title(self):
+ self.title = _('{0} with {1}').format(self.patient_name or self.patient,
+ self.practitioner_name or self.practitioner)[:100]
+
def create_therapy_plan(encounter):
if len(encounter.therapies):
doc = frappe.new_doc('Therapy Plan')
diff --git a/erpnext/healthcare/doctype/sample_collection/sample_collection.json b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
index 39cead8..c352287 100644
--- a/erpnext/healthcare/doctype/sample_collection/sample_collection.json
+++ b/erpnext/healthcare/doctype/sample_collection/sample_collection.json
@@ -9,14 +9,14 @@
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
- "inpatient_record",
"naming_series",
- "invoiced",
"patient",
- "column_break_4",
"patient_age",
"patient_sex",
+ "column_break_4",
+ "inpatient_record",
"company",
+ "invoiced",
"section_break_6",
"sample",
"sample_uom",
@@ -167,7 +167,7 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2020-03-25 16:55:52.376834",
+ "modified": "2020-04-04 19:17:02.707203",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Sample Collection",
diff --git a/erpnext/healthcare/doctype/therapy_session/therapy_session.js b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
index abe4def..e66e667 100644
--- a/erpnext/healthcare/doctype/therapy_session/therapy_session.js
+++ b/erpnext/healthcare/doctype/therapy_session/therapy_session.js
@@ -9,6 +9,16 @@
{fieldname: 'counts_completed', columns: 1},
{fieldname: 'assistance_level', columns: 1}
];
+
+ frm.set_query('service_unit', function() {
+ return {
+ filters: {
+ 'is_group': false,
+ 'allow_appointments': true,
+ 'company': frm.doc.company
+ }
+ };
+ });
},
refresh: function(frm) {
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.json b/erpnext/healthcare/doctype/vital_signs/vital_signs.json
index 75726db..15ab504 100644
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.json
+++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.json
@@ -2,18 +2,22 @@
"actions": [],
"allow_copy": 1,
"allow_import": 1,
+ "autoname": "naming_series:",
"beta": 1,
"creation": "2017-02-02 11:00:24.853005",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "inpatient_record",
+ "naming_series",
+ "title",
"patient",
"patient_name",
+ "inpatient_record",
"appointment",
"encounter",
"column_break_2",
+ "company",
"signs_date",
"signs_time",
"sb_vs",
@@ -34,7 +38,7 @@
"bmi",
"column_break_14",
"nutrition_note",
- "company",
+ "sb_references",
"amended_from"
],
"fields": [
@@ -68,7 +72,8 @@
"fieldname": "appointment",
"fieldtype": "Link",
"in_filter": 1,
- "label": "Appointment",
+ "label": "Patient Appointment",
+ "no_copy": 1,
"options": "Patient Appointment",
"print_hide": 1,
"read_only": 1
@@ -81,8 +86,7 @@
"no_copy": 1,
"options": "Patient Encounter",
"print_hide": 1,
- "read_only": 1,
- "report_hide": 1
+ "read_only": 1
},
{
"fieldname": "column_break_2",
@@ -217,7 +221,6 @@
{
"fieldname": "company",
"fieldtype": "Link",
- "hidden": 1,
"label": "Company",
"options": "Company"
},
@@ -229,11 +232,34 @@
"options": "Vital Signs",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "sb_references",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Select",
+ "label": "Series",
+ "options": "HLC-VTS-.YYYY.-",
+ "reqd": 1
+ },
+ {
+ "allow_on_submit": 1,
+ "columns": 5,
+ "fieldname": "title",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Title",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1
}
],
"is_submittable": 1,
"links": [],
- "modified": "2020-03-04 17:19:29.549889",
+ "modified": "2020-05-17 22:23:24.632286",
"modified_by": "Administrator",
"module": "Healthcare",
"name": "Vital Signs",
@@ -273,7 +299,7 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "patient",
+ "title_field": "title",
"track_changes": 1,
"track_seen": 1
}
\ No newline at end of file
diff --git a/erpnext/healthcare/doctype/vital_signs/vital_signs.py b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
index b0e78e8..69d81ff 100644
--- a/erpnext/healthcare/doctype/vital_signs/vital_signs.py
+++ b/erpnext/healthcare/doctype/vital_signs/vital_signs.py
@@ -9,12 +9,19 @@
from frappe import _
class VitalSigns(Document):
+ def validate(self):
+ self.set_title()
+
def on_submit(self):
insert_vital_signs_to_medical_record(self)
def on_cancel(self):
delete_vital_signs_from_medical_record(self)
+ def set_title(self):
+ self.title = _('{0} on {1}').format(self.patient_name or self.patient,
+ frappe.utils.format_date(self.signs_date))[:100]
+
def insert_vital_signs_to_medical_record(doc):
subject = set_subject_field(doc)
medical_record = frappe.new_doc('Patient Medical Record')
diff --git a/erpnext/healthcare/utils.py b/erpnext/healthcare/utils.py
index a756532..f092578 100644
--- a/erpnext/healthcare/utils.py
+++ b/erpnext/healthcare/utils.py
@@ -3,83 +3,84 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+import math
import frappe
from frappe import _
-import math
from frappe.utils import time_diff_in_hours, rounded
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_income_account
from erpnext.healthcare.doctype.fee_validity.fee_validity import create_fee_validity
from erpnext.healthcare.doctype.lab_test.lab_test import create_multiple
@frappe.whitelist()
-def get_healthcare_services_to_invoice(patient):
+def get_healthcare_services_to_invoice(patient, company):
patient = frappe.get_doc('Patient', patient)
+ items_to_invoice = []
if patient:
validate_customer_created(patient)
- items_to_invoice = []
- patient_appointments = frappe.get_list(
- 'Patient Appointment',
- fields='*',
- filters={'patient': patient.name, 'invoiced': 0},
- order_by='appointment_date'
- )
- if patient_appointments:
- items_to_invoice = get_fee_validity(patient_appointments)
+ # Customer validated, build a list of billable services
+ items_to_invoice += get_appointments_to_invoice(patient, company)
+ items_to_invoice += get_encounters_to_invoice(patient, company)
+ items_to_invoice += get_lab_tests_to_invoice(patient, company)
+ items_to_invoice += get_clinical_procedures_to_invoice(patient, company)
+ items_to_invoice += get_inpatient_services_to_invoice(patient, company)
+ items_to_invoice += get_therapy_sessions_to_invoice(patient, company)
- encounters = get_encounters_to_invoice(patient)
- lab_tests = get_lab_tests_to_invoice(patient)
- clinical_procedures = get_clinical_procedures_to_invoice(patient)
- inpatient_services = get_inpatient_services_to_invoice(patient)
- therapy_sessions = get_therapy_sessions_to_invoice(patient)
- items_to_invoice += encounters + lab_tests + clinical_procedures + inpatient_services + therapy_sessions
return items_to_invoice
+
def validate_customer_created(patient):
if not frappe.db.get_value('Patient', patient.name, 'customer'):
msg = _("Please set a Customer linked to the Patient")
msg += " <b><a href='#Form/Patient/{0}'>{0}</a></b>".format(patient.name)
frappe.throw(msg, title=_('Customer Not Found'))
-def get_fee_validity(patient_appointments):
- if not frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups'):
- return []
+def get_appointments_to_invoice(patient, company):
+ appointments_to_invoice = []
+ patient_appointments = frappe.get_list(
+ 'Patient Appointment',
+ fields = '*',
+ filters = {'patient': patient.name, 'company': company, 'invoiced': 0},
+ order_by = 'appointment_date'
+ )
- items_to_invoice = []
for appointment in patient_appointments:
+ # Procedure Appointments
if appointment.procedure_template:
if frappe.db.get_value('Clinical Procedure Template', appointment.procedure_template, 'is_billable'):
- items_to_invoice.append({
+ appointments_to_invoice.append({
'reference_type': 'Patient Appointment',
'reference_name': appointment.name,
'service': appointment.procedure_template
})
+ # Consultation Appointments, should check fee validity
else:
- fee_validity = frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name})
- if not fee_validity:
- practitioner_charge = 0
- income_account = None
- service_item = None
- if appointment.practitioner:
- service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
- income_account = get_income_account(appointment.practitioner, appointment.company)
- items_to_invoice.append({
- 'reference_type': 'Patient Appointment',
- 'reference_name': appointment.name,
- 'service': service_item,
- 'rate': practitioner_charge,
- 'income_account': income_account
- })
+ if frappe.db.get_single_value('Healthcare Settings', 'enable_free_follow_ups') and \
+ frappe.db.exists('Fee Validity Reference', {'appointment': appointment.name}):
+ continue # Skip invoicing, fee validty present
+ practitioner_charge = 0
+ income_account = None
+ service_item = None
+ if appointment.practitioner:
+ service_item, practitioner_charge = get_service_item_and_practitioner_charge(appointment)
+ income_account = get_income_account(appointment.practitioner, appointment.company)
+ appointments_to_invoice.append({
+ 'reference_type': 'Patient Appointment',
+ 'reference_name': appointment.name,
+ 'service': service_item,
+ 'rate': practitioner_charge,
+ 'income_account': income_account
+ })
- return items_to_invoice
+ return appointments_to_invoice
-def get_encounters_to_invoice(patient):
+def get_encounters_to_invoice(patient, company):
encounters_to_invoice = []
encounters = frappe.get_list(
'Patient Encounter',
fields=['*'],
- filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
+ filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
)
if encounters:
for encounter in encounters:
@@ -102,12 +103,12 @@
return encounters_to_invoice
-def get_lab_tests_to_invoice(patient):
+def get_lab_tests_to_invoice(patient, company):
lab_tests_to_invoice = []
lab_tests = frappe.get_list(
'Lab Test',
fields=['name', 'template'],
- filters={'patient': patient.name, 'invoiced': False, 'docstatus': 1}
+ filters={'patient': patient.name, 'company': company, 'invoiced': False, 'docstatus': 1}
)
for lab_test in lab_tests:
item, is_billable = frappe.get_cached_value('Lab Test Template', lab_test.template, ['item', 'is_billable'])
@@ -143,12 +144,12 @@
return lab_tests_to_invoice
-def get_clinical_procedures_to_invoice(patient):
+def get_clinical_procedures_to_invoice(patient, company):
clinical_procedures_to_invoice = []
procedures = frappe.get_list(
'Clinical Procedure',
fields='*',
- filters={'patient': patient.name, 'invoiced': False}
+ filters={'patient': patient.name, 'company': company, 'invoiced': False}
)
for procedure in procedures:
if not procedure.appointment:
@@ -204,7 +205,7 @@
return clinical_procedures_to_invoice
-def get_inpatient_services_to_invoice(patient):
+def get_inpatient_services_to_invoice(patient, company):
services_to_invoice = []
inpatient_services = frappe.db.sql(
'''
@@ -214,10 +215,11 @@
`tabInpatient Record` ip, `tabInpatient Occupancy` io
WHERE
ip.patient=%s
+ and ip.company=%s
and io.parent=ip.name
and io.left=1
and io.invoiced=0
- ''', (patient.name), as_dict=1)
+ ''', (patient.name, company), as_dict=1)
for inpatient_occupancy in inpatient_services:
service_unit_type = frappe.db.get_value('Healthcare Service Unit', inpatient_occupancy.service_unit, 'service_unit_type')
@@ -244,12 +246,12 @@
return services_to_invoice
-def get_therapy_sessions_to_invoice(patient):
+def get_therapy_sessions_to_invoice(patient, company):
therapy_sessions_to_invoice = []
therapy_sessions = frappe.get_list(
'Therapy Session',
fields='*',
- filters={'patient': patient.name, 'invoiced': False}
+ filters={'patient': patient.name, 'invoiced': 0, 'company': company}
)
for therapy in therapy_sessions:
if not therapy.appointment:
@@ -396,6 +398,7 @@
def manage_fee_validity(appointment):
fee_validity = check_fee_validity(appointment)
+
if fee_validity:
if appointment.status == 'Cancelled' and fee_validity.visited > 0:
fee_validity.visited -= 1
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index 6b198e7..ab161aa 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -308,7 +308,8 @@
"erpnext.crm.doctype.email_campaign.email_campaign.send_email_to_leads_or_contacts",
"erpnext.crm.doctype.email_campaign.email_campaign.set_email_campaign_status",
"erpnext.selling.doctype.quotation.quotation.set_expired_status",
- "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status"
+ "erpnext.healthcare.doctype.patient_appointment.patient_appointment.update_appointment_status",
+ "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status"
],
"daily_long": [
"erpnext.setup.doctype.email_digest.email_digest.send",
diff --git a/erpnext/hr/doctype/attendance/attendance.py b/erpnext/hr/doctype/attendance/attendance.py
index b6c8065..6c7ee00 100644
--- a/erpnext/hr/doctype/attendance/attendance.py
+++ b/erpnext/hr/doctype/attendance/attendance.py
@@ -21,7 +21,7 @@
date_of_joining = frappe.db.get_value("Employee", self.employee, "date_of_joining")
# leaves can be marked for future dates
- if self.status not in ('On Leave', 'Half Day') and getdate(self.attendance_date) > getdate(nowdate()):
+ if self.status != 'On Leave' and not self.leave_application and getdate(self.attendance_date) > getdate(nowdate()):
frappe.throw(_("Attendance can not be marked for future dates"))
elif date_of_joining and getdate(self.attendance_date) < getdate(date_of_joining):
frappe.throw(_("Attendance date can not be less than employee's joining date"))
@@ -41,7 +41,7 @@
leave_record = frappe.db.sql("""
select leave_type, half_day, half_day_date
from `tabLeave Application`
- where employee = %s
+ where employee = %s
and %s between from_date and to_date
and status = 'Approved'
and docstatus = 1
diff --git a/erpnext/hr/doctype/department/department.json b/erpnext/hr/doctype/department/department.json
index 6469f4c..a54c1d1 100644
--- a/erpnext/hr/doctype/department/department.json
+++ b/erpnext/hr/doctype/department/department.json
@@ -14,6 +14,8 @@
"is_group",
"disabled",
"section_break_4",
+ "payroll_cost_center",
+ "column_break_9",
"leave_block_list",
"leave_section",
"leave_approvers",
@@ -125,13 +127,23 @@
{
"fieldname": "column_break_3",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "payroll_cost_center",
+ "fieldtype": "Link",
+ "label": "Payroll Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "column_break_9",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-sitemap",
"idx": 1,
"is_tree": 1,
"links": [],
- "modified": "2020-03-18 18:03:27.784362",
+ "modified": "2020-05-05 18:49:28.503931",
"modified_by": "Administrator",
"module": "HR",
"name": "Department",
diff --git a/erpnext/hr/doctype/employee/employee.json b/erpnext/hr/doctype/employee/employee.json
index 13c202c..f575765 100644
--- a/erpnext/hr/doctype/employee/employee.json
+++ b/erpnext/hr/doctype/employee/employee.json
@@ -60,6 +60,8 @@
"default_shift",
"salary_information",
"salary_mode",
+ "payroll_cost_center",
+ "column_break_52",
"bank_name",
"bank_ac_no",
"health_insurance_section",
@@ -783,13 +785,25 @@
{
"fieldname": "column_break_19",
"fieldtype": "Column Break"
+ },
+ {
+ "fetch_from": "department.payroll_cost_center",
+ "fetch_if_empty": 1,
+ "fieldname": "payroll_cost_center",
+ "fieldtype": "Link",
+ "label": "Payroll Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "column_break_52",
+ "fieldtype": "Column Break"
}
],
"icon": "fa fa-user",
"idx": 24,
"image_field": "image",
"links": [],
- "modified": "2020-04-08 12:25:34.306695",
+ "modified": "2020-05-05 18:51:03.152503",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee",
diff --git a/erpnext/hr/doctype/employee/test_employee.py b/erpnext/hr/doctype/employee/test_employee.py
index d3410de..f4b214a 100644
--- a/erpnext/hr/doctype/employee/test_employee.py
+++ b/erpnext/hr/doctype/employee/test_employee.py
@@ -45,7 +45,7 @@
employee1_doc.status = 'Left'
self.assertRaises(EmployeeLeftValidationError, employee1_doc.save)
-def make_employee(user, company=None):
+def make_employee(user, company=None, **kwargs):
if not frappe.db.get_value("User", user):
frappe.get_doc({
"doctype": "User",
@@ -55,7 +55,7 @@
"roles": [{"doctype": "Has Role", "role": "Employee"}]
}).insert()
- if not frappe.db.get_value("Employee", { "user_id": user, "company": company or erpnext.get_default_company() }):
+ if not frappe.db.get_value("Employee", {"user_id": user}):
employee = frappe.get_doc({
"doctype": "Employee",
"naming_series": "EMP-",
@@ -71,7 +71,10 @@
"prefered_email": user,
"status": "Active",
"employment_type": "Intern"
- }).insert()
+ })
+ if kwargs:
+ employee.update(kwargs)
+ employee.insert()
return employee.name
else:
return frappe.get_value("Employee", {"employee_name":user}, "name")
diff --git a/erpnext/hr/doctype/employee_other_income/employee_other_income.json b/erpnext/hr/doctype/employee_other_income/employee_other_income.json
index 2dd6c10..8abfe1e 100644
--- a/erpnext/hr/doctype/employee_other_income/employee_other_income.json
+++ b/erpnext/hr/doctype/employee_other_income/employee_other_income.json
@@ -76,25 +76,15 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2020-03-19 18:06:45.361830",
+ "modified": "2020-05-14 17:17:38.883126",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Other Income",
"owner": "Administrator",
"permissions": [
{
- "create": 1,
- "delete": 1,
- "email": 1,
- "export": 1,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "share": 1,
- "write": 1
- },
- {
+ "amend": 1,
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -104,9 +94,12 @@
"report": 1,
"role": "HR Manager",
"share": 1,
+ "submit": 1,
"write": 1
},
{
+ "amend": 1,
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -116,9 +109,12 @@
"report": 1,
"role": "HR User",
"share": 1,
+ "submit": 1,
"write": 1
},
{
+ "amend": 1,
+ "cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
@@ -128,6 +124,7 @@
"report": 1,
"role": "Employee",
"share": 1,
+ "submit": 1,
"write": 1
}
],
diff --git a/erpnext/hr/doctype/expense_claim/expense_claim.py b/erpnext/hr/doctype/expense_claim/expense_claim.py
index ac1bfa1..ea469b8 100644
--- a/erpnext/hr/doctype/expense_claim/expense_claim.py
+++ b/erpnext/hr/doctype/expense_claim/expense_claim.py
@@ -116,8 +116,9 @@
"party_type": "Employee",
"party": self.employee,
"against_voucher_type": self.doctype,
- "against_voucher": self.name
- })
+ "against_voucher": self.name,
+ "cost_center": self.cost_center
+ }, item=self)
)
# expense entries
@@ -129,7 +130,7 @@
"debit_in_account_currency": data.sanctioned_amount,
"against": self.employee,
"cost_center": data.cost_center
- })
+ }, item=data)
)
for data in self.advances:
@@ -157,7 +158,7 @@
"credit": self.grand_total,
"credit_in_account_currency": self.grand_total,
"against": self.employee
- })
+ }, item=self)
)
gl_entry.append(
@@ -170,7 +171,7 @@
"debit_in_account_currency": self.grand_total,
"against_voucher": self.name,
"against_voucher_type": self.doctype,
- })
+ }, item=self)
)
return gl_entry
@@ -187,7 +188,7 @@
"cost_center": self.cost_center,
"against_voucher_type": self.doctype,
"against_voucher": self.name
- })
+ }, item=tax)
)
def validate_account_details(self):
diff --git a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
index 16e9eef..3cce50e 100644
--- a/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
+++ b/erpnext/hr/doctype/expense_claim_detail/expense_claim_detail.json
@@ -13,9 +13,11 @@
"description",
"section_break_6",
"amount",
- "cost_center",
"column_break_8",
- "sanctioned_amount"
+ "sanctioned_amount",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break"
],
"fields": [
{
@@ -104,12 +106,21 @@
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
+ },
+ {
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2019-12-11 13:42:33.233432",
+ "modified": "2020-05-11 18:54:35.601592",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Detail",
diff --git a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
index d68caf1..885e3ee 100644
--- a/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
+++ b/erpnext/hr/doctype/expense_taxes_and_charges/expense_taxes_and_charges.json
@@ -8,14 +8,16 @@
"engine": "InnoDB",
"field_order": [
"account_head",
- "cost_center",
"rate",
"col_break1",
"description",
"section_break_6",
"tax_amount",
"column_break_8",
- "total"
+ "total",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break"
],
"fields": [
{
@@ -91,11 +93,20 @@
{
"fieldname": "column_break_8",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
}
],
"istable": 1,
"links": [],
- "modified": "2020-03-11 13:25:06.721917",
+ "modified": "2020-05-11 19:01:26.611758",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Taxes and Charges",
diff --git a/erpnext/hr/doctype/leave_application/leave_application.py b/erpnext/hr/doctype/leave_application/leave_application.py
index 47b1bb7..d2620be 100755
--- a/erpnext/hr/doctype/leave_application/leave_application.py
+++ b/erpnext/hr/doctype/leave_application/leave_application.py
@@ -549,7 +549,7 @@
return _get_remaining_leaves(total_leaves, allocation.to_date)
-def get_leaves_for_period(employee, leave_type, from_date, to_date):
+def get_leaves_for_period(employee, leave_type, from_date, to_date, do_not_skip_expired_leaves=False):
leave_entries = get_leave_entries(employee, leave_type, from_date, to_date)
leave_days = 0
@@ -559,8 +559,8 @@
if inclusive_period and leave_entry.transaction_type == 'Leave Encashment':
leave_days += leave_entry.leaves
- elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' \
- and leave_entry.is_expired and not skip_expiry_leaves(leave_entry, to_date):
+ elif inclusive_period and leave_entry.transaction_type == 'Leave Allocation' and leave_entry.is_expired \
+ and (do_not_skip_expired_leaves or not skip_expiry_leaves(leave_entry, to_date)):
leave_days += leave_entry.leaves
elif leave_entry.transaction_type == 'Leave Application':
diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
index 9ed58c9..63559c4 100644
--- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
+++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py
@@ -88,32 +88,40 @@
}, fieldname=['name'])
def process_expired_allocation():
- ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry '''
+ ''' Check if a carry forwarded allocation has expired and create a expiry ledger entry
+ Case 1: carry forwarded expiry period is set for the leave type,
+ create a separate leave expiry entry against each entry of carry forwarded and non carry forwarded leaves
+ Case 2: leave type has no specific expiry period for carry forwarded leaves
+ and there is no carry forwarded leave allocation, create a single expiry against the remaining leaves.
+ '''
# fetch leave type records that has carry forwarded leaves expiry
leave_type_records = frappe.db.get_values("Leave Type", filters={
'expire_carry_forwarded_leaves_after_days': (">", 0)
}, fieldname=['name'])
- leave_type = [record[0] for record in leave_type_records]
+ leave_type = [record[0] for record in leave_type_records] or ['']
- expired_allocation = frappe.db.sql_list("""SELECT name
- FROM `tabLeave Ledger Entry`
- WHERE
- `transaction_type`='Leave Allocation'
- AND `is_expired`=1""")
-
- expire_allocation = frappe.get_all("Leave Ledger Entry",
- fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'],
- filters={
- 'to_date': ("<", today()),
- 'transaction_type': 'Leave Allocation',
- 'transaction_name': ('not in', expired_allocation)
- },
- or_filters={
- 'is_carry_forward': 0,
- 'leave_type': ('in', leave_type)
- })
+ # fetch non expired leave ledger entry of transaction_type allocation
+ expire_allocation = frappe.db.sql("""
+ SELECT
+ leaves, to_date, employee, leave_type,
+ is_carry_forward, transaction_name as name, transaction_type
+ FROM `tabLeave Ledger Entry` l
+ WHERE (NOT EXISTS
+ (SELECT name
+ FROM `tabLeave Ledger Entry`
+ WHERE
+ transaction_name = l.transaction_name
+ AND transaction_type = 'Leave Allocation'
+ AND name<>l.name
+ AND docstatus = 1
+ AND (
+ is_carry_forward=l.is_carry_forward
+ OR (is_carry_forward = 0 AND leave_type not in %s)
+ )))
+ AND transaction_type = 'Leave Allocation'
+ AND to_date < %s""", (leave_type, today()), as_dict=1)
if expire_allocation:
create_expiry_ledger_entry(expire_allocation)
@@ -133,6 +141,7 @@
'employee': allocation.employee,
'leave_type': allocation.leave_type,
'to_date': ('<=', allocation.to_date),
+ 'docstatus': 1
}, fieldname=['SUM(leaves)'])
@frappe.whitelist()
@@ -159,7 +168,8 @@
def expire_carried_forward_allocation(allocation):
''' Expires remaining leaves in the on carried forward allocation '''
from erpnext.hr.doctype.leave_application.leave_application import get_leaves_for_period
- leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type, allocation.from_date, allocation.to_date)
+ leaves_taken = get_leaves_for_period(allocation.employee, allocation.leave_type,
+ allocation.from_date, allocation.to_date, do_not_skip_expired_leaves=True)
leaves = flt(allocation.leaves) + flt(leaves_taken)
# allow expired leaves entry to be created
diff --git a/erpnext/hr/doctype/payroll_entry/payroll_entry.py b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
index 9ef3a99..656de01 100644
--- a/erpnext/hr/doctype/payroll_entry/payroll_entry.py
+++ b/erpnext/hr/doctype/payroll_entry/payroll_entry.py
@@ -55,6 +55,7 @@
ifnull(salary_slip_based_on_timesheet,0) = %(salary_slip_based_on_timesheet)s
{condition}""".format(condition=condition),
{"company": self.company, "salary_slip_based_on_timesheet":self.salary_slip_based_on_timesheet})
+
if sal_struct:
cond += "and t2.salary_structure IN %(sal_struct)s "
cond += "and %(from_date)s >= t2.from_date"
@@ -138,7 +139,7 @@
cond = self.get_filter_condition()
ss_list = frappe.db.sql("""
- select t1.name, t1.salary_structure from `tabSalary Slip` t1
+ select t1.name, t1.salary_structure, t1.payroll_cost_center from `tabSalary Slip` t1
where t1.docstatus = %s and t1.start_date >= %s and t1.end_date <= %s
and (t1.journal_entry is null or t1.journal_entry = "") and ifnull(salary_slip_based_on_timesheet,0) = %s %s
""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
@@ -169,10 +170,14 @@
def get_salary_components(self, component_type):
salary_slips = self.get_sal_slip_list(ss_status = 1, as_dict = True)
- if salary_slips:
- salary_components = frappe.db.sql("""select salary_component, amount, parentfield
- from `tabSalary Detail` where parentfield = '%s' and parent in (%s)""" %
- (component_type, ', '.join(['%s']*len(salary_slips))), tuple([d.name for d in salary_slips]), as_dict=True)
+ if salary_slips:
+ salary_components = frappe.db.sql("""
+ select ssd.salary_component, ssd.amount, ssd.parentfield, ss.payroll_cost_center
+ from `tabSalary Slip` ss, `tabSalary Detail` ssd
+ where ss.name = ssd.parent and ssd.parentfield = '%s' and ss.name in (%s)
+ """ % (component_type, ', '.join(['%s']*len(salary_slips))),
+ tuple([d.name for d in salary_slips]), as_dict=True)
+
return salary_components
def get_salary_component_total(self, component_type = None):
@@ -186,15 +191,16 @@
if is_flexible_benefit == 1 and only_tax_impact ==1:
add_component_to_accrual_jv_entry = False
if add_component_to_accrual_jv_entry:
- component_dict[item['salary_component']] = component_dict.get(item['salary_component'], 0) + item['amount']
+ component_dict[(item.salary_component, item.payroll_cost_center)] \
+ = component_dict.get((item.salary_component, item.payroll_cost_center), 0) + flt(item.amount)
account_details = self.get_account(component_dict = component_dict)
return account_details
def get_account(self, component_dict = None):
- account_dict = {}
- for s, a in component_dict.items():
- account = self.get_salary_component_account(s)
- account_dict[account] = account_dict.get(account, 0) + a
+ account_dict = {}
+ for key, amount in component_dict.items():
+ account = self.get_salary_component_account(key[0])
+ account_dict[(account, key[1])] = account_dict.get((account, key[1]), 0) + amount
return account_dict
def get_default_payroll_payable_account(self):
@@ -227,23 +233,23 @@
payable_amount = 0
# Earnings
- for acc, amount in earnings.items():
+ for acc_cc, amount in earnings.items():
payable_amount += flt(amount, precision)
accounts.append({
- "account": acc,
+ "account": acc_cc[0],
"debit_in_account_currency": flt(amount, precision),
"party_type": '',
- "cost_center": self.cost_center,
+ "cost_center": acc_cc[1] or self.cost_center,
"project": self.project
})
# Deductions
- for acc, amount in deductions.items():
+ for acc_cc, amount in deductions.items():
payable_amount -= flt(amount, precision)
accounts.append({
- "account": acc,
+ "account": acc_cc[0],
"credit_in_account_currency": flt(amount, precision),
- "cost_center": self.cost_center,
+ "cost_center": acc_cc[1] or self.cost_center,
"party_type": '',
"project": self.project
})
@@ -253,6 +259,7 @@
"account": default_payroll_payable_account,
"credit_in_account_currency": flt(payable_amount, precision),
"party_type": '',
+ "cost_center": self.cost_center
})
journal_entry.set("accounts", accounts)
diff --git a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
index e43f744..3c318e7 100644
--- a/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
+++ b/erpnext/hr/doctype/payroll_entry/test_payroll_entry.py
@@ -10,15 +10,16 @@
from erpnext.hr.doctype.payroll_entry.payroll_entry import get_start_end_dates, get_end_date
from erpnext.hr.doctype.employee.test_employee import make_employee
from erpnext.hr.doctype.salary_slip.test_salary_slip import get_salary_component_account, \
- make_earning_salary_component, make_deduction_salary_component
+ make_earning_salary_component, make_deduction_salary_component, create_account
from erpnext.hr.doctype.salary_structure.test_salary_structure import make_salary_structure
from erpnext.loan_management.doctype.loan.test_loan import create_loan, make_loan_disbursement_entry
from erpnext.loan_management.doctype.process_loan_interest_accrual.process_loan_interest_accrual import process_loan_interest_accrual_for_term_loans
class TestPayrollEntry(unittest.TestCase):
def setUp(self):
- for dt in ["Salary Slip", "Salary Component", "Salary Component Account", "Payroll Entry", "Salary Structure"]:
- frappe.db.sql("delete from `tab%s`" % dt)
+ for dt in ["Salary Slip", "Salary Component", "Salary Component Account",
+ "Payroll Entry", "Salary Structure", "Salary Structure Assignment", "Payroll Employee Detail", "Additional Salary"]:
+ frappe.db.sql("delete from `tab%s`" % dt)
make_earning_salary_component(setup=True, company_list=["_Test Company"])
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
@@ -33,11 +34,59 @@
get_salary_component_account(data.name)
employee = frappe.db.get_value("Employee", {'company': company})
- make_salary_structure("_Test Salary Structure", "Monthly", employee)
+ make_salary_structure("_Test Salary Structure", "Monthly", employee, company=company)
dates = get_start_end_dates('Monthly', nowdate())
if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date)
+ def test_payroll_entry_with_employee_cost_center(self): # pylint: disable=no-self-use
+ for data in frappe.get_all('Salary Component', fields = ["name"]):
+ if not frappe.db.get_value('Salary Component Account',
+ {'parent': data.name, 'company': "_Test Company"}, 'name'):
+ get_salary_component_account(data.name)
+
+ if not frappe.db.exists('Department', "cc - _TC"):
+ frappe.get_doc({
+ 'doctype': 'Department',
+ 'department_name': "cc",
+ "company": "_Test Company"
+ }).insert()
+
+ employee1 = make_employee("test_employee1@example.com", payroll_cost_center="_Test Cost Center - _TC",
+ department="cc - _TC", company="_Test Company")
+ employee2 = make_employee("test_employee2@example.com", payroll_cost_center="_Test Cost Center 2 - _TC",
+ department="cc - _TC", company="_Test Company")
+
+ make_salary_structure("_Test Salary Structure 1", "Monthly", employee1, company="_Test Company")
+ make_salary_structure("_Test Salary Structure 2", "Monthly", employee2, company="_Test Company")
+
+ if not frappe.db.exists("Account", "_Test Payroll Payable - _TC"):
+ create_account(account_name="_Test Payroll Payable",
+ company="_Test Company", parent_account="Current Liabilities - _TC")
+ frappe.db.set_value("Company", "_Test Company", "default_payroll_payable_account",
+ "_Test Payroll Payable - _TC")
+
+ dates = get_start_end_dates('Monthly', nowdate())
+ if not frappe.db.get_value("Salary Slip", {"start_date": dates.start_date, "end_date": dates.end_date}):
+ pe = make_payroll_entry(start_date=dates.start_date, end_date=dates.end_date,
+ department="cc - _TC", company="_Test Company", payment_account="Cash - _TC", cost_center="Main - _TC")
+ je = frappe.db.get_value("Salary Slip", {"payroll_entry": pe.name}, "journal_entry")
+ je_entries = frappe.db.sql("""
+ select account, cost_center, debit, credit
+ from `tabJournal Entry Account`
+ where parent=%s
+ order by account, cost_center
+ """, je)
+ expected_je = (
+ ('_Test Payroll Payable - _TC', 'Main - _TC', 0.0, 155600.0),
+ ('Salary - _TC', '_Test Cost Center - _TC', 78000.0, 0.0),
+ ('Salary - _TC', '_Test Cost Center 2 - _TC', 78000.0, 0.0),
+ ('Salary Deductions - _TC', '_Test Cost Center - _TC', 0.0, 200.0),
+ ('Salary Deductions - _TC', '_Test Cost Center 2 - _TC', 0.0, 200.0)
+ )
+
+ self.assertEqual(je_entries, expected_je)
+
def test_get_end_date(self):
self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
@@ -49,7 +98,6 @@
self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
def test_loan(self):
-
branch = "Test Employee Branch"
applicant = make_employee("test_employee@loan.com", company="_Test Company")
company = "_Test Company"
@@ -116,6 +164,7 @@
payroll_entry.posting_date = nowdate()
payroll_entry.payroll_frequency = "Monthly"
payroll_entry.branch = args.branch or None
+ payroll_entry.department = args.department or None
if args.cost_center:
payroll_entry.cost_center = args.cost_center
@@ -123,6 +172,7 @@
if args.payment_account:
payroll_entry.payment_account = args.payment_account
+ payroll_entry.fill_employee_details()
payroll_entry.save()
payroll_entry.create_salary_slips()
payroll_entry.submit_salary_slips()
diff --git a/erpnext/hr/doctype/salary_slip/salary_slip.json b/erpnext/hr/doctype/salary_slip/salary_slip.json
index 54a8164..cfd4d89 100644
--- a/erpnext/hr/doctype/salary_slip/salary_slip.json
+++ b/erpnext/hr/doctype/salary_slip/salary_slip.json
@@ -12,6 +12,7 @@
"department",
"designation",
"branch",
+ "payroll_cost_center",
"column_break1",
"status",
"journal_entry",
@@ -459,13 +460,22 @@
"options": "Salary Slip",
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fetch_from": "employee.payroll_cost_center",
+ "fetch_if_empty": 1,
+ "fieldname": "payroll_cost_center",
+ "fieldtype": "Link",
+ "label": "Payroll Cost Center",
+ "options": "Cost Center",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 9,
"is_submittable": 1,
"links": [],
- "modified": "2020-04-14 20:02:53.159827",
+ "modified": "2020-05-05 18:55:26.173629",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",
diff --git a/erpnext/hr/doctype/salary_slip/test_salary_slip.py b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
index a7dcb94..3eff738 100644
--- a/erpnext/hr/doctype/salary_slip/test_salary_slip.py
+++ b/erpnext/hr/doctype/salary_slip/test_salary_slip.py
@@ -422,22 +422,32 @@
sal_comp = frappe.get_doc("Salary Component", sal_comp)
if not sal_comp.get("accounts"):
for d in company_list:
+ company_abbr = frappe.get_cached_value('Company', d, 'abbr')
+
+ if sal_comp.type == "Earning":
+ account_name = "Salary"
+ parent_account = "Indirect Expenses - " + company_abbr
+ else:
+ account_name = "Salary Deductions"
+ parent_account = "Current Liabilities - " + company_abbr
+
sal_comp.append("accounts", {
"company": d,
- "default_account": create_account(d)
+ "default_account": create_account(account_name, d, parent_account)
})
sal_comp.save()
-def create_account(company):
- salary_account = frappe.db.get_value("Account", "Salary - " + frappe.get_cached_value('Company', company, 'abbr'))
- if not salary_account:
+def create_account(account_name, company, parent_account):
+ company_abbr = frappe.get_cached_value('Company', company, 'abbr')
+ account = frappe.db.get_value("Account", account_name + " - " + company_abbr)
+ if not account:
frappe.get_doc({
"doctype": "Account",
- "account_name": "Salary",
- "parent_account": "Indirect Expenses - " + frappe.get_cached_value('Company', company, 'abbr'),
+ "account_name": account_name,
+ "parent_account": parent_account,
"company": company
}).insert()
- return salary_account
+ return account
def make_earning_salary_component(setup=False, test_tax=False, company_list=None):
data = [
@@ -683,7 +693,7 @@
make_earning_salary_component(setup=True, company_list=["_Test Company"])
make_deduction_salary_component(setup=True, company_list=["_Test Company"])
- for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance"]:
+ for dt in ["Leave Application", "Leave Allocation", "Salary Slip", "Attendance", "Additional Salary"]:
frappe.db.sql("delete from `tab%s`" % dt)
make_holiday_list()
diff --git a/erpnext/hr/doctype/salary_structure/salary_structure.py b/erpnext/hr/doctype/salary_structure/salary_structure.py
index 5ba7f1c..ffc16d7 100644
--- a/erpnext/hr/doctype/salary_structure/salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/salary_structure.py
@@ -153,12 +153,16 @@
def postprocess(source, target):
if employee:
employee_details = frappe.db.get_value("Employee", employee,
- ["employee_name", "branch", "designation", "department"], as_dict=1)
+ ["employee_name", "branch", "designation", "department", "payroll_cost_center"], as_dict=1)
target.employee = employee
target.employee_name = employee_details.employee_name
target.branch = employee_details.branch
target.designation = employee_details.designation
target.department = employee_details.department
+ target.payroll_cost_center = employee_details.payroll_cost_center
+ if not target.payroll_cost_center and target.department:
+ target.payroll_cost_center = frappe.db.get_value("Department", target.department, "payroll_cost_center")
+
target.run_method('process_salary_structure', for_preview=for_preview)
doc = get_mapped_doc("Salary Structure", source_name, {
diff --git a/erpnext/hr/doctype/salary_structure/test_salary_structure.py b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
index c1869f0..eb5311e 100644
--- a/erpnext/hr/doctype/salary_structure/test_salary_structure.py
+++ b/erpnext/hr/doctype/salary_structure/test_salary_structure.py
@@ -128,6 +128,7 @@
salary_structure_doc.insert()
if not dont_submit:
salary_structure_doc.submit()
+
else:
salary_structure_doc = frappe.get_doc("Salary Structure", salary_structure)
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index f721724..4ae591b 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -623,7 +623,7 @@
erpnext.patches.v12_0.update_due_date_in_gle
erpnext.patches.v12_0.add_default_buying_selling_terms_in_company
erpnext.patches.v12_0.update_ewaybill_field_position
-erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes
+erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes #2020-05-11
erpnext.patches.v11_1.set_status_for_material_request_type_manufacture
erpnext.patches.v12_0.move_plaid_settings_to_doctype
execute:frappe.reload_doc('desk', 'doctype', 'dashboard_chart_link')
@@ -678,6 +678,10 @@
erpnext.patches.v12_0.fix_quotation_expired_status
erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
erpnext.patches.v12_0.retain_permission_rules_for_video_doctype
+erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
execute:frappe.delete_doc_if_exists("Page", "appointment-analytic")
execute:frappe.rename_doc("Desk Page", "Getting Started", "Home", force=True)
+erpnext.patches.v12_0.unset_customer_supplier_based_on_type_of_item_price
+erpnext.patches.v12_0.set_valid_till_date_in_supplier_quotation
+erpnext.patches.v12_0.set_serial_no_status
diff --git a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
index b71ea66..657decf 100644
--- a/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
+++ b/erpnext/patches/v12_0/create_accounting_dimensions_in_missing_doctypes.py
@@ -20,7 +20,8 @@
else:
insert_after_field = 'accounting_dimensions_section'
- for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item"]:
+ for doctype in ["Subscription Plan", "Subscription", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item",
+ "Expense Claim Detail", "Expense Taxes and Charges"]:
field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
diff --git a/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
new file mode 100644
index 0000000..98a2fcf
--- /dev/null
+++ b/erpnext/patches/v12_0/remove_duplicate_leave_ledger_entries.py
@@ -0,0 +1,44 @@
+# Copyright (c) 2018, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ """Delete duplicate leave ledger entries of type allocation created."""
+ if not frappe.db.a_row_exists("Leave Ledger Entry"):
+ return
+
+ duplicate_records_list = get_duplicate_records()
+ delete_duplicate_ledger_entries(duplicate_records_list)
+
+def get_duplicate_records():
+ """Fetch all but one duplicate records from the list of expired leave allocation."""
+ return frappe.db.sql_list("""
+ WITH duplicate_records AS
+ (SELECT
+ name, transaction_name, is_carry_forward,
+ ROW_NUMBER() over(partition by transaction_name order by creation)as row
+ FROM `tabLeave Ledger Entry` l
+ WHERE (EXISTS
+ (SELECT name
+ FROM `tabLeave Ledger Entry`
+ WHERE
+ transaction_name = l.transaction_name
+ AND transaction_type = 'Leave Allocation'
+ AND name <> l.name
+ AND employee = l.employee
+ AND docstatus = 1
+ AND leave_type = l.leave_type
+ AND is_carry_forward=l.is_carry_forward
+ AND to_date = l.to_date
+ AND from_date = l.from_date
+ AND is_expired = 1
+ )))
+ SELECT name FROM duplicate_records WHERE row > 1
+ """)
+
+def delete_duplicate_ledger_entries(duplicate_records_list):
+ """Delete duplicate leave ledger entries."""
+ if duplicate_records_list:
+ frappe.db.sql(''' DELETE FROM `tabLeave Ledger Entry` WHERE name in {0}'''.format(tuple(duplicate_records_list))) #nosec
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_serial_no_status.py b/erpnext/patches/v12_0/set_serial_no_status.py
new file mode 100644
index 0000000..4ec84ef
--- /dev/null
+++ b/erpnext/patches/v12_0/set_serial_no_status.py
@@ -0,0 +1,17 @@
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import getdate, nowdate
+
+def execute():
+ frappe.reload_doc('stock', 'doctype', 'serial_no')
+
+ for serial_no in frappe.db.sql("""select name, delivery_document_type, warranty_expiry_date from `tabSerial No`
+ where (status is NULL OR status='')""", as_dict = 1):
+ if serial_no.get("delivery_document_type"):
+ status = "Delivered"
+ elif serial_no.get("warranty_expiry_date") and getdate(serial_no.get("warranty_expiry_date")) <= getdate(nowdate()):
+ status = "Expired"
+ else:
+ status = "Active"
+
+ frappe.db.set_value("Serial No", serial_no.get("name"), "status", status)
\ No newline at end of file
diff --git a/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
new file mode 100644
index 0000000..4a6e228
--- /dev/null
+++ b/erpnext/patches/v12_0/set_valid_till_date_in_supplier_quotation.py
@@ -0,0 +1,8 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc("buying", "doctype", "supplier_quotation")
+ frappe.db.sql("""UPDATE `tabSupplier Quotation`
+ SET valid_till = DATE_ADD(transaction_date , INTERVAL 1 MONTH)
+ WHERE docstatus < 2""")
diff --git a/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
new file mode 100644
index 0000000..60aec05
--- /dev/null
+++ b/erpnext/patches/v12_0/unset_customer_supplier_based_on_type_of_item_price.py
@@ -0,0 +1,15 @@
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ invalid_selling_item_price = frappe.db.sql(
+ """SELECT name FROM `tabItem Price` WHERE selling = 1 and buying = 0 and (supplier IS NOT NULL or supplier = '')"""
+ )
+ invalid_buying_item_price = frappe.db.sql(
+ """SELECT name FROM `tabItem Price` WHERE selling = 0 and buying = 1 and (customer IS NOT NULL or customer = '')"""
+ )
+ docs_to_modify = invalid_buying_item_price + invalid_selling_item_price
+ for d in docs_to_modify:
+ # saving the doc will auto reset invalid customer/supplier field
+ doc = frappe.get_doc("Item Price", d[0])
+ doc.save()
\ No newline at end of file
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 3309858..732780a 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -251,8 +251,7 @@
def calculate_annual_eligible_hra_exemption(doc):
- basic_component = frappe.get_cached_value('Company', doc.company, "basic_component")
- hra_component = frappe.get_cached_value('Company', doc.company, "hra_component")
+ basic_component, hra_component = frappe.db.get_value('Company', doc.company, ["basic_component", "hra_component"])
if not (basic_component and hra_component):
frappe.throw(_("Please mention Basic and HRA component in Company"))
annual_exemption, monthly_exemption, hra_amount = 0, 0, 0
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 50e719f..3d172ac 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -165,6 +165,10 @@
contact.mobile_no = lead.mobile_no
contact.is_primary_contact = 1
contact.append('links', dict(link_doctype='Customer', link_name=self.name))
+ if lead.email_id:
+ contact.append('email_ids', dict(email_id=lead.email_id, is_primary=1))
+ if lead.mobile_no:
+ contact.append('phone_nos', dict(phone=lead.mobile_no, is_primary_mobile_no=1))
contact.flags.ignore_permissions = self.flags.ignore_permissions
contact.autoname()
if not frappe.db.exists("Contact", contact.name):
diff --git a/erpnext/shopping_cart/cart.py b/erpnext/shopping_cart/cart.py
index e11e1bb..4ac546e 100644
--- a/erpnext/shopping_cart/cart.py
+++ b/erpnext/shopping_cart/cart.py
@@ -541,27 +541,31 @@
return doc.tc_name
@frappe.whitelist(allow_guest=True)
-def apply_coupon_code(applied_code,applied_referral_sales_partner):
+def apply_coupon_code(applied_code, applied_referral_sales_partner):
quotation = True
- if applied_code:
- coupon_list=frappe.get_all('Coupon Code', filters={"docstatus": ("<", "2"), 'coupon_code':applied_code }, fields=['name'])
- if coupon_list:
- coupon_name=coupon_list[0].name
- from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
- validate_coupon_code(coupon_name)
- quotation = _get_cart_quotation()
- quotation.coupon_code=coupon_name
+
+ if not applied_code:
+ frappe.throw(_("Please enter a coupon code"))
+
+ coupon_list = frappe.get_all('Coupon Code', filters={'coupon_code': applied_code})
+ if not coupon_list:
+ frappe.throw(_("Please enter a valid coupon code"))
+
+ coupon_name = coupon_list[0].name
+
+ from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
+ validate_coupon_code(coupon_name)
+ quotation = _get_cart_quotation()
+ quotation.coupon_code = coupon_name
+ quotation.flags.ignore_permissions = True
+ quotation.save()
+
+ if applied_referral_sales_partner:
+ sales_partner_list = frappe.get_all('Sales Partner', filters={'referral_code': applied_referral_sales_partner})
+ if sales_partner_list:
+ sales_partner_name = sales_partner_list[0].name
+ quotation.referral_sales_partner = sales_partner_name
quotation.flags.ignore_permissions = True
quotation.save()
- if applied_referral_sales_partner:
- sales_partner_list=frappe.get_all('Sales Partner', filters={'docstatus': 0, 'referral_code':applied_referral_sales_partner }, fields=['name'])
- if sales_partner_list:
- sales_partner_name=sales_partner_list[0].name
- quotation.referral_sales_partner=sales_partner_name
- quotation.flags.ignore_permissions = True
- quotation.save()
- else:
- frappe.throw(_("Please enter valid coupon code !!"))
- else:
- frappe.throw(_("Please enter coupon code !!"))
+
return quotation
diff --git a/erpnext/shopping_cart/product_info.py b/erpnext/shopping_cart/product_info.py
index a7da09c..21ee335 100644
--- a/erpnext/shopping_cart/product_info.py
+++ b/erpnext/shopping_cart/product_info.py
@@ -10,14 +10,16 @@
from erpnext.utilities.product import get_price, get_qty_in_stock, get_non_stock_item_status
@frappe.whitelist(allow_guest=True)
-def get_product_info_for_website(item_code):
+def get_product_info_for_website(item_code, skip_quotation_creation=False):
"""get product price / stock info for website"""
cart_settings = get_shopping_cart_settings()
if not cart_settings.enabled:
return frappe._dict()
- cart_quotation = _get_cart_quotation()
+ cart_quotation = frappe._dict()
+ if not skip_quotation_creation:
+ cart_quotation = _get_cart_quotation()
price = get_price(
item_code,
@@ -51,7 +53,7 @@
def set_product_info_for_website(item):
"""set product price uom for website"""
- product_info = get_product_info_for_website(item.item_code)
+ product_info = get_product_info_for_website(item.item_code, skip_quotation_creation=True)
if product_info:
item.update(product_info)
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 4cc50bb..7a1c127 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -467,7 +467,7 @@
def set_shopping_cart_data(self, context):
from erpnext.shopping_cart.product_info import get_product_info_for_website
- context.shopping_cart = get_product_info_for_website(self.name)
+ context.shopping_cart = get_product_info_for_website(self.name, skip_quotation_creation=True)
def add_default_uom_in_conversion_factor_table(self):
uom_conv_list = [d.uom for d in self.get("uoms")]
diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py
index 957c415..8e39eb5 100644
--- a/erpnext/stock/doctype/item_price/item_price.py
+++ b/erpnext/stock/doctype/item_price/item_price.py
@@ -69,3 +69,10 @@
self.reference = self.customer
if self.buying:
self.reference = self.supplier
+
+ if self.selling and not self.buying:
+ # if only selling then remove supplier
+ self.supplier = None
+ if self.buying and not self.selling:
+ # if only buying then remove customer
+ self.customer = None
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 62c9eb1..be2dd52 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -363,6 +363,9 @@
+ self.work_order + ":" + ", ".join(other_ste), DuplicateEntryForWorkOrderError)
def set_incoming_rate(self):
+ if self.purpose == "Repack":
+ self.set_basic_rate_for_finished_goods()
+
for d in self.items:
if d.s_warehouse:
args = self.get_args_for_incoming_rate(d)
@@ -475,20 +478,31 @@
"allow_zero_valuation": item.allow_zero_valuation_rate,
})
- def set_basic_rate_for_finished_goods(self, raw_material_cost, scrap_material_cost):
+ def set_basic_rate_for_finished_goods(self, raw_material_cost=0, scrap_material_cost=0):
+ total_fg_qty = 0
+ if not raw_material_cost and self.get("items"):
+ raw_material_cost = sum([flt(row.basic_amount) for row in self.items
+ if row.s_warehouse and not row.t_warehouse])
+
+ total_fg_qty = sum([flt(row.qty) for row in self.items
+ if row.t_warehouse and not row.s_warehouse])
+
if self.purpose in ["Manufacture", "Repack"]:
for d in self.get("items"):
if (d.transfer_qty and (d.bom_no or d.t_warehouse)
and (getattr(self, "pro_doc", frappe._dict()).scrap_warehouse != d.t_warehouse)):
- if self.work_order \
- and frappe.db.get_single_value("Manufacturing Settings", "material_consumption"):
+ if (self.work_order and self.purpose == "Manufacture"
+ and frappe.db.get_single_value("Manufacturing Settings", "material_consumption")):
bom_items = self.get_bom_raw_materials(d.transfer_qty)
raw_material_cost = sum([flt(row.qty)*flt(row.rate) for row in bom_items.values()])
- if raw_material_cost:
+ if raw_material_cost and self.purpose == "Manufacture":
d.basic_rate = flt((raw_material_cost - scrap_material_cost) / flt(d.transfer_qty), d.precision("basic_rate"))
d.basic_amount = flt((raw_material_cost - scrap_material_cost), d.precision("basic_amount"))
+ elif self.purpose == "Repack" and total_fg_qty:
+ d.basic_rate = flt(raw_material_cost) / flt(total_fg_qty)
+ d.basic_amount = d.basic_rate * d.qty
def distribute_additional_costs(self):
if self.purpose == "Material Issue":
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index d50712a..11b6403 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -630,7 +630,7 @@
elif args.get("supplier"):
conditions += " and supplier=%(supplier)s"
else:
- conditions += " and (customer is null or customer = '') and (supplier is null or supplier = '')"
+ conditions += "and (customer is null or customer = '') and (supplier is null or supplier = '')"
if args.get('transaction_date'):
conditions += """ and %(transaction_date)s between