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",
|
"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]"
|
"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,
|
"hidden": 0,
|
||||||
"label": "Rehabilitation and Physiotherapy",
|
"label": "Rehabilitation and Physiotherapy",
|
||||||
@ -38,7 +43,7 @@
|
|||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"label": "Records and History",
|
"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,
|
"hidden": 0,
|
||||||
@ -64,7 +69,7 @@
|
|||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
"label": "Healthcare",
|
"label": "Healthcare",
|
||||||
"modified": "2020-11-23 23:00:48.764377",
|
"modified": "2020-11-26 22:09:09.164584",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Healthcare",
|
"module": "Healthcare",
|
||||||
"name": "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) {
|
patient: function(frm) {
|
||||||
|
@ -142,25 +142,32 @@ class InpatientMedicationEntry(Document):
|
|||||||
return orders, order_entry_map
|
return orders, order_entry_map
|
||||||
|
|
||||||
def check_stock_qty(self):
|
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()
|
if drug_shortage:
|
||||||
for d in self.medication_orders:
|
message = _('Quantity not available for the following items in warehouse {0}. ').format(frappe.bold(self.warehouse))
|
||||||
if not drug_availability.get(d.drug_code):
|
message += _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.')
|
||||||
drug_availability[d.drug_code] = 0
|
|
||||||
drug_availability[d.drug_code] += flt(d.dosage)
|
|
||||||
|
|
||||||
for drug, dosage in drug_availability.items():
|
formatted_item_rows = ''
|
||||||
available_qty = get_latest_stock_qty(drug, self.warehouse)
|
|
||||||
|
|
||||||
# validate qty
|
for drug, shortage_qty in drug_shortage.items():
|
||||||
if flt(available_qty) < flt(dosage):
|
item_link = get_link_to_form('Item', drug)
|
||||||
frappe.throw(_('Quantity not available for {0} in warehouse {1}').format(
|
formatted_item_rows += """
|
||||||
frappe.bold(drug), frappe.bold(self.warehouse))
|
<td>{0}</td>
|
||||||
+ '<br><br>' + _('Available quantity is {0}, you need {1}').format(
|
<td>{1}</td>
|
||||||
frappe.bold(available_qty), frappe.bold(dosage))
|
</tr>""".format(item_link, frappe.bold(shortage_qty))
|
||||||
+ '<br><br>' + _('Please enable Allow Negative Stock in Stock Settings or create Stock Entry to proceed.'),
|
|
||||||
NegativeStockError, title=_('Insufficient Stock'))
|
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):
|
def make_stock_entry(self):
|
||||||
stock_entry = frappe.new_doc('Stock Entry')
|
stock_entry = frappe.new_doc('Stock Entry')
|
||||||
@ -223,6 +230,7 @@ def get_pending_medication_orders(entry):
|
|||||||
|
|
||||||
for doc in data:
|
for doc in data:
|
||||||
inpatient_record = doc.inpatient_record
|
inpatient_record = doc.inpatient_record
|
||||||
|
if inpatient_record:
|
||||||
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
doc['service_unit'] = get_current_healthcare_service_unit(inpatient_record)
|
||||||
|
|
||||||
if entry.service_unit and doc.service_unit != entry.service_unit:
|
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:
|
if ip_record.inpatient_occupancies:
|
||||||
return ip_record.inpatient_occupancies[-1].service_unit
|
return ip_record.inpatient_occupancies[-1].service_unit
|
||||||
return
|
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.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_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_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
|
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_account
|
||||||
|
|
||||||
class TestInpatientMedicationEntry(unittest.TestCase):
|
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].patient, self.patient)
|
||||||
self.assertEqual(stock_entry.items[0].inpatient_medication_entry_child, ipme.medication_orders[0].name)
|
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):
|
def tearDown(self):
|
||||||
# cleanup - Discharge
|
# cleanup - Discharge
|
||||||
schedule_discharge(frappe.as_json({'patient': self.patient}))
|
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'):
|
for entry in frappe.get_all('Inpatient Medication Entry'):
|
||||||
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
doc = frappe.get_doc('Inpatient Medication Entry', entry.name)
|
||||||
doc.cancel()
|
doc.cancel()
|
||||||
frappe.db.delete('Stock Entry', {'inpatient_medication_entry': doc.name})
|
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
for entry in frappe.get_all('Inpatient Medication Order'):
|
for entry in frappe.get_all('Inpatient Medication Order'):
|
||||||
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
doc = frappe.get_doc('Inpatient Medication Order', entry.name)
|
||||||
doc.cancel()
|
doc.cancel()
|
||||||
doc.delete()
|
|
||||||
|
|
||||||
def make_stock_entry():
|
def make_stock_entry(warehouse=None):
|
||||||
frappe.db.set_value('Company', '_Test Company', {
|
frappe.db.set_value('Company', '_Test Company', {
|
||||||
'stock_adjustment_account': 'Stock Adjustment - _TC',
|
'stock_adjustment_account': 'Stock Adjustment - _TC',
|
||||||
'default_inventory_account': 'Stock In Hand - _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 = frappe.new_doc('Stock Entry')
|
||||||
stock_entry.stock_entry_type = 'Material Receipt'
|
stock_entry.stock_entry_type = 'Material Receipt'
|
||||||
stock_entry.company = '_Test Company'
|
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')
|
expense_account = get_account(None, 'expense_account', 'Healthcare Settings', '_Test Company')
|
||||||
se_child = stock_entry.append('items')
|
se_child = stock_entry.append('items')
|
||||||
se_child.item_code = 'Dextromethorphan'
|
se_child.item_code = 'Dextromethorphan'
|
||||||
|
@ -18,6 +18,10 @@ def get_data():
|
|||||||
{
|
{
|
||||||
'label': _('Billing'),
|
'label': _('Billing'),
|
||||||
'items': ['Sales Invoice']
|
'items': ['Sales Invoice']
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'label': _('Orders'),
|
||||||
|
'items': ['Inpatient Medication Order']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user