Merge pull request #24012 from ruchamahabal/stock-entry-for-medication-entries
feat(IPME): Button to create Stock Entry for Drug Shortage
This commit is contained in:
commit
0953058650
@ -30,6 +30,11 @@
|
||||
"label": "Laboratory",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Lab Test\",\n\t\t\"label\": \"Lab Test\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Sample Collection\",\n\t\t\"label\": \"Sample Collection\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Dosage Form\",\n\t\t\"label\": \"Dosage Form\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Inpatient",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Order\",\n\t\t\"label\": \"Inpatient Medication Order\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Medication Entry\",\n\t\t\"label\": \"Inpatient Medication Entry\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Rehabilitation and Physiotherapy",
|
||||
@ -38,7 +43,7 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"label": "Records and History",
|
||||
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Inpatient Record\",\n\t\t\"label\": \"Inpatient Record\"\n\t}\n]"
|
||||
"links": "[\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient_history\",\n\t\t\"label\": \"Patient History\"\n\t},\n\t{\n\t\t\"type\": \"page\",\n\t\t\"name\": \"patient-progress\",\n\t\t\"label\": \"Patient Progress\"\n\t},\n\t{\n\t\t\"type\": \"doctype\",\n\t\t\"name\": \"Patient Medical Record\",\n\t\t\"label\": \"Patient Medical Record\"\n\t}\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
@ -64,7 +69,7 @@
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Healthcare",
|
||||
"modified": "2020-11-23 23:00:48.764377",
|
||||
"modified": "2020-11-26 22:09:09.164584",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Healthcare",
|
||||
"name": "Healthcare",
|
||||
|
@ -29,6 +29,29 @@ frappe.ui.form.on('Inpatient Medication Entry', {
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
if (frm.doc.__islocal || frm.doc.docstatus !== 0 || !frm.doc.update_stock)
|
||||
return;
|
||||
|
||||
frm.add_custom_button(__('Make Stock Entry'), function() {
|
||||
frappe.call({
|
||||
method: 'erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry.make_difference_stock_entry',
|
||||
args: { docname: frm.doc.name },
|
||||
freeze: true,
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
var doclist = frappe.model.sync(r.message);
|
||||
frappe.set_route('Form', doclist[0].doctype, doclist[0].name);
|
||||
} else {
|
||||
frappe.msgprint({
|
||||
title: __('No Drug Shortage'),
|
||||
message: __('All the drugs are available with sufficient qty to process this Inpatient Medication Entry.'),
|
||||
indicator: 'green'
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
patient: function(frm) {
|
||||
|
@ -142,25 +142,32 @@ class InpatientMedicationEntry(Document):
|
||||
return orders, order_entry_map
|
||||
|
||||
def check_stock_qty(self):
|
||||
from erpnext.stock.stock_ledger import NegativeStockError
|
||||
drug_shortage = get_drug_shortage_map(self.medication_orders, self.warehouse)
|
||||
|
||||
drug_availability = dict()
|
||||
for d in self.medication_orders:
|
||||
if not drug_availability.get(d.drug_code):
|
||||
drug_availability[d.drug_code] = 0
|
||||
drug_availability[d.drug_code] += flt(d.dosage)
|
||||
if drug_shortage:
|
||||
message = _('Quantity not available for the following items in warehouse {0}. ').format(frappe.bold(self.warehouse))
|
||||
message += _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.')
|
||||
|
||||
for drug, dosage in drug_availability.items():
|
||||
available_qty = get_latest_stock_qty(drug, self.warehouse)
|
||||
formatted_item_rows = ''
|
||||
|
||||
# validate qty
|
||||
if flt(available_qty) < flt(dosage):
|
||||
frappe.throw(_('Quantity not available for {0} in warehouse {1}').format(
|
||||
frappe.bold(drug), frappe.bold(self.warehouse))
|
||||
+ '<br><br>' + _('Available quantity is {0}, you need {1}').format(
|
||||
frappe.bold(available_qty), frappe.bold(dosage))
|
||||
+ '<br><br>' + _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.'),
|
||||
NegativeStockError, title=_('Insufficient Stock'))
|
||||
for drug, shortage_qty in drug_shortage.items():
|
||||
item_link = get_link_to_form('Item', drug)
|
||||
formatted_item_rows += """
|
||||
<td>{0}</td>
|
||||
<td>{1}</td>
|
||||
</tr>""".format(item_link, frappe.bold(shortage_qty))
|
||||
|
||||
message += """
|
||||
<table class='table'>
|
||||
<thead>
|
||||
<th>{0}</th>
|
||||
<th>{1}</th>
|
||||
</thead>
|
||||
{2}
|
||||
</table>
|
||||
""".format(_('Drug Code'), _('Shortage Qty'), formatted_item_rows)
|
||||
|
||||
frappe.throw(message, title=_('Insufficient Stock'), is_minimizable=True, wide=True)
|
||||
|
||||
def make_stock_entry(self):
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
@ -223,6 +230,7 @@ def get_pending_medication_orders(entry):
|
||||
|
||||
for doc in data:
|
||||
inpatient_record = doc.inpatient_record
|
||||
if inpatient_record:
|
||||
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
||||
|
||||
if entry.service_unit and doc.service_unit != entry.service_unit:
|
||||
@ -277,3 +285,54 @@ def get_current_healthcare_service_unit(inpatient_record):
|
||||
if ip_record.inpatient_occupancies:
|
||||
return ip_record.inpatient_occupancies[-1].service_unit
|
||||
return
|
||||
|
||||
|
||||
def get_drug_shortage_map(medication_orders, warehouse):
|
||||
"""
|
||||
Returns a dict like { drug_code: shortage_qty }
|
||||
"""
|
||||
drug_requirement = dict()
|
||||
for d in medication_orders:
|
||||
if not drug_requirement.get(d.drug_code):
|
||||
drug_requirement[d.drug_code] = 0
|
||||
drug_requirement[d.drug_code] += flt(d.dosage)
|
||||
|
||||
drug_shortage = dict()
|
||||
for drug, required_qty in drug_requirement.items():
|
||||
available_qty = get_latest_stock_qty(drug, warehouse)
|
||||
if flt(required_qty) > flt(available_qty):
|
||||
drug_shortage[drug] = flt(flt(required_qty) - flt(available_qty))
|
||||
|
||||
return drug_shortage
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_difference_stock_entry(docname):
|
||||
doc = frappe.get_doc('Inpatient Medication Entry', docname)
|
||||
drug_shortage = get_drug_shortage_map(doc.medication_orders, doc.warehouse)
|
||||
|
||||
if not drug_shortage:
|
||||
return None
|
||||
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
stock_entry.purpose = 'Material Transfer'
|
||||
stock_entry.set_stock_entry_type()
|
||||
stock_entry.to_warehouse = doc.warehouse
|
||||
stock_entry.company = doc.company
|
||||
cost_center = frappe.get_cached_value('Company', doc.company, 'cost_center')
|
||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', doc.company)
|
||||
|
||||
for drug, shortage_qty in drug_shortage.items():
|
||||
se_child = stock_entry.append('items')
|
||||
se_child.item_code = drug
|
||||
se_child.item_name = frappe.db.get_value('Item', drug, 'stock_uom')
|
||||
se_child.uom = frappe.db.get_value('Item', drug, 'stock_uom')
|
||||
se_child.stock_uom = se_child.uom
|
||||
se_child.qty = flt(shortage_qty)
|
||||
se_child.t_warehouse = doc.warehouse
|
||||
# in stock uom
|
||||
se_child.conversion_factor = 1
|
||||
se_child.cost_center = cost_center
|
||||
se_child.expense_account = expense_account
|
||||
|
||||
return stock_entry
|
||||
|
@ -9,6 +9,7 @@ from frappe.utils import add_days, getdate, now_datetime
|
||||
from erpnext.healthcare.doctype.inpatient_record.test_inpatient_record import create_patient, create_inpatient, get_healthcare_service_unit, mark_invoiced_inpatient_occupancy
|
||||
from erpnext.healthcare.doctype.inpatient_record.inpatient_record import admit_patient, discharge_patient, schedule_discharge
|
||||
from erpnext.healthcare.doctype.inpatient_medication_order.test_inpatient_medication_order import create_ipmo, create_ipme
|
||||
from erpnext.healthcare.doctype.inpatient_medication_entry.inpatient_medication_entry import get_drug_shortage_map, make_difference_stock_entry
|
||||
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
|
||||
|
||||
class TestInpatientMedicationEntry(unittest.TestCase):
|
||||
@ -82,6 +83,39 @@ class TestInpatientMedicationEntry(unittest.TestCase):
|
||||
self.assertEqual(stock_entry.items[0].patient, self.patient)
|
||||
self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name)
|
||||
|
||||
def test_drug_shortage_stock_entry(self):
|
||||
ipmo = create_ipmo(self.patient)
|
||||
ipmo.submit()
|
||||
ipmo.reload()
|
||||
|
||||
date = add_days(getdate(), -1)
|
||||
filters = frappe._dict(
|
||||
from_date=date,
|
||||
to_date=date,
|
||||
from_time='',
|
||||
to_time='',
|
||||
item_code='Dextromethorphan',
|
||||
patient=self.patient
|
||||
)
|
||||
|
||||
# check drug shortage
|
||||
ipme = create_ipme(filters, update_stock=1)
|
||||
ipme.warehouse = 'Finished Goods - _TC'
|
||||
ipme.save()
|
||||
drug_shortage = get_drug_shortage_map(ipme.medication_orders, ipme.warehouse)
|
||||
self.assertEqual(drug_shortage.get('Dextromethorphan'), 3)
|
||||
|
||||
# check material transfer for drug shortage
|
||||
make_stock_entry()
|
||||
stock_entry = make_difference_stock_entry(ipme.name)
|
||||
self.assertEqual(stock_entry.items[0].item_code, 'Dextromethorphan')
|
||||
self.assertEqual(stock_entry.items[0].qty, 3)
|
||||
stock_entry.from_warehouse = 'Stores - _TC'
|
||||
stock_entry.submit()
|
||||
|
||||
ipme.reload()
|
||||
ipme.submit()
|
||||
|
||||
def tearDown(self):
|
||||
# cleanup - Discharge
|
||||
schedule_discharge(frappe.as_json({'patient': self.patient}))
|
||||
@ -94,15 +128,12 @@ class TestInpatientMedicationEntry(unittest.TestCase):
|
||||
for entry in frappe.get_all('Inpatient Medication Entry'):
|
||||
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
||||
doc.cancel()
|
||||
frappe.db.delete('Stock Entry', {'inpatient_medication_entry': doc.name})
|
||||
doc.delete()
|
||||
|
||||
for entry in frappe.get_all('Inpatient Medication Order'):
|
||||
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
||||
doc.cancel()
|
||||
doc.delete()
|
||||
|
||||
def make_stock_entry():
|
||||
def make_stock_entry(warehouse=None):
|
||||
frappe.db.set_value('Company', '_Test Company', {
|
||||
'stock_adjustment_account': 'Stock Adjustment - _TC',
|
||||
'default_inventory_account': 'Stock In Hand - _TC'
|
||||
@ -110,7 +141,7 @@ def make_stock_entry():
|
||||
stock_entry = frappe.new_doc('Stock Entry')
|
||||
stock_entry.stock_entry_type = 'Material Receipt'
|
||||
stock_entry.company = '_Test Company'
|
||||
stock_entry.to_warehouse = 'Stores - _TC'
|
||||
stock_entry.to_warehouse = warehouse or 'Stores - _TC'
|
||||
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company')
|
||||
se_child = stock_entry.append('items')
|
||||
se_child.item_code = 'Dextromethorphan'
|
||||
|
@ -18,6 +18,10 @@ def get_data():
|
||||
{
|
||||
'label': _('Billing'),
|
||||
'items': ['Sales Invoice']
|
||||
},
|
||||
{
|
||||
'label': _('Orders'),
|
||||
'items': ['Inpatient Medication Order']
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user