fix(medical-store): remove third-party brand Marg from application and replace with generic Excel/CSV import
This commit is contained in:
parent
e635e823e5
commit
ca6926015a
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
### HMS Admin Management
|
||||
- **URL:** [http://localhost:8080/setting/admin/medical-store](http://localhost:8080/setting/admin/medical-store)
|
||||
- **Features:** Store configuration, DL 20B/21B licenses, GSTIN, Pharmacist details, ABDM HFR/HIP/HPR registries, 60-min OTP generator, terminal device authorization, and Marg ERP CSV import.
|
||||
- **Features:** Store configuration, DL 20B/21B licenses, GSTIN, Pharmacist details, ABDM HFR/HIP/HPR registries, 60-min OTP generator, terminal device authorization, and Excel / CSV stock import.
|
||||
|
||||
### Direct Counter Desks (React PWA)
|
||||
- **Counter A (Main Hospital Block A):**
|
||||
|
|
@ -59,15 +59,15 @@
|
|||
- 80mm Thermal Slip (Fast 1–5 item OTC/OPD).
|
||||
- A5 Invoice (Normal OPD/Walk-in with GST summary).
|
||||
- A4 Hospital Tax Invoice (IPD admissions & large bills).
|
||||
4. **Marg ERP Migration:**
|
||||
- Supported CSV import with auto-detected columns. Sample test file: `scratch/test_marg_sample.csv`.
|
||||
4. **Excel / CSV Stock Migration:**
|
||||
- Supported CSV import with auto-detected columns.
|
||||
5. **Double-Entry Accounting & Statutory Drug Register:**
|
||||
- Automated ledger postings, Daily Cash Book, GSTR-1 outward reports, and mandatory Schedule H1 drug register.
|
||||
6. **Retail Loose Tablet & Unit Dispensing:**
|
||||
- Exact per-tablet pricing (`Strip MRP / units_per_pack`), exact stock deduction in individual tablets, and support for Loose Tab, Strip, and Combo sales.
|
||||
7. **Split / Mixed Payment Mode & Audit:**
|
||||
- Supports partial payments (e.g. ₹35 Cash + ₹100 UPI) with 12-digit UTR and bank name capturing for bank reconciliation and audit.
|
||||
8. **Keyboard-First Zero-Mouse POS Workflow (Marg ERP Standard):**
|
||||
8. **Keyboard-First Zero-Mouse POS Workflow (Desktop Pharmacy Standard):**
|
||||
- High-contrast single-line Quick Keys ribbon.
|
||||
- Hotkeys: `F2` (Medicine Search), `F4` (Patient Search), `F7` (Whole Discount), `F8` (Payment Mode), `F9 / Ctrl+Enter` (Complete Sale & Print), `Alt+T` (Loose Tab), `Alt+S` (Strip), `Shift+Delete` (Remove Item).
|
||||
- Auto-focus on Medicine Search on page load; `Enter` on quantity immediately commits and jumps focus back to Medicine Lookup for the next scan.
|
||||
|
|
|
|||
|
|
@ -1426,7 +1426,7 @@ $routes->group('setting', static function($routes) {
|
|||
$routes->post('admin/medical-store/generate-otp', 'Setting\\MedicalStoreAdmin::generateOtp');
|
||||
$routes->post('admin/medical-store/regenerate-key', 'Setting\\MedicalStoreAdmin::regenerateSecurityKey');
|
||||
$routes->post('admin/medical-store/revoke-device', 'Setting\\MedicalStoreAdmin::revokeDevice');
|
||||
$routes->post('admin/medical-store/import-marg', 'Setting\\MedicalStoreAdmin::importMarg');
|
||||
$routes->post('admin/medical-store/import-excel', 'Setting\\MedicalStoreAdmin::importExcel');
|
||||
|
||||
$routes->get('admin/bed-management', 'Setting\\BedManagement::index', ['filter' => $settingsBedFilter]);
|
||||
$routes->get('admin/departments', 'Setting\\BedManagement::departments', ['filter' => $settingsBedFilter]);
|
||||
|
|
@ -1702,7 +1702,7 @@ $routes->group('api/v1', ['namespace' => 'App\Controllers\Api\v1'], static funct
|
|||
$routes->get('medical-store/compliance/schedule-h1-register', 'MedicalStoreApi::getScheduleH1Register');
|
||||
$routes->post('medical-store/device/verify', 'MedicalStoreApi::verifyDevice');
|
||||
$routes->post('medical-store/device/check', 'MedicalStoreApi::checkDeviceStatus');
|
||||
$routes->post('medical-store/stock/import-marg', 'MedicalStoreApi::importMarg');
|
||||
$routes->post('medical-store/stock/import-excel', 'MedicalStoreApi::importExcel');
|
||||
$routes->get('medical-store/abdm/bundle/(:num)', 'MedicalStoreApi::getAbdmBundle/$1');
|
||||
$routes->post('medical-store/abdm/link-abha', 'MedicalStoreApi::linkAbha');
|
||||
$routes->get('medical-store/returns/lookup-invoice', 'MedicalStoreApi::lookupInvoiceForReturn');
|
||||
|
|
|
|||
|
|
@ -3481,10 +3481,10 @@ class MedicalStoreApi extends BaseController
|
|||
return $this->response->setJSON(['status' => 0, 'authorized' => false, 'message' => 'Device authorization revoked or not found.']);
|
||||
}
|
||||
|
||||
public function importMarg()
|
||||
public function importExcel()
|
||||
{
|
||||
$admin = new \App\Controllers\Setting\MedicalStoreAdmin();
|
||||
return $admin->importMarg();
|
||||
return $admin->importExcel();
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
|
|
|
|||
|
|
@ -235,9 +235,9 @@ class MedicalStoreAdmin extends BaseController
|
|||
}
|
||||
|
||||
/**
|
||||
* Marg Pharmacy Software / Excel CSV Inventory Import
|
||||
* Excel / CSV Inventory Import
|
||||
*/
|
||||
public function importMarg()
|
||||
public function importExcel()
|
||||
{
|
||||
$storeId = (int)($this->request->getPost('store_id') ?? 0);
|
||||
if ($storeId <= 0) {
|
||||
|
|
@ -251,7 +251,7 @@ class MedicalStoreAdmin extends BaseController
|
|||
|
||||
$ext = strtolower($file->getClientExtension());
|
||||
if (!in_array($ext, ['csv', 'txt'])) {
|
||||
return $this->response->setJSON(['ok' => false, 'error' => 'Please upload CSV formatted export from Marg ERP or Excel.']);
|
||||
return $this->response->setJSON(['ok' => false, 'error' => 'Please upload CSV formatted export from Excel or CSV file.']);
|
||||
}
|
||||
|
||||
$handle = fopen($file->getTempName(), 'r');
|
||||
|
|
@ -322,7 +322,7 @@ class MedicalStoreAdmin extends BaseController
|
|||
$qty = ($colQty !== null) ? (int)preg_replace('/[^0-9]/', '', $row[$colQty] ?? '10') : 10;
|
||||
$barcode = ($colBarcode !== null) ? trim($row[$colBarcode] ?? '') : '';
|
||||
|
||||
// Parse Marg Expiry formats (MM/YY, MM/YYYY, DD-MM-YYYY, YYYY-MM-DD)
|
||||
// Parse standard date and expiry formats (MM/YY, MM/YYYY, DD-MM-YYYY, YYYY-MM-DD)
|
||||
$expiryDate = date('Y-12-31', strtotime('+1 year'));
|
||||
if (!empty($expRaw)) {
|
||||
if (preg_match('/^(\d{1,2})[\/\-](\d{2})$/', $expRaw, $m)) {
|
||||
|
|
@ -418,7 +418,7 @@ class MedicalStoreAdmin extends BaseController
|
|||
|
||||
// Record opening stock valuation journal voucher in double-entry ledgers
|
||||
if ($totalValuation > 0) {
|
||||
$voucherNo = 'MARG-IMP-' . $storeId . '-' . date('YmdHis');
|
||||
$voucherNo = 'CSV-IMP-' . $storeId . '-' . date('YmdHis');
|
||||
$stockHead = $this->db->table('mst_account_heads')->where('head_code', '1003')->get()->getRowArray();
|
||||
$equityHead = $this->db->table('mst_account_heads')->where('head_code', '3004')->get()->getRowArray();
|
||||
|
||||
|
|
@ -426,12 +426,12 @@ class MedicalStoreAdmin extends BaseController
|
|||
$this->db->table('mst_ledger_entries')->insert([
|
||||
'store_id' => $storeId, 'voucher_no' => $voucherNo, 'voucher_type' => 'OPENING_STOCK', 'voucher_date' => date('Y-m-d'),
|
||||
'account_head_id' => $stockHead['head_id'], 'debit_amount' => $totalValuation, 'credit_amount' => 0,
|
||||
'narration' => "Marg ERP Opening Stock Inward ($importedRows lines)"
|
||||
'narration' => "Excel/CSV Opening Stock Inward ($importedRows lines)"
|
||||
]);
|
||||
$this->db->table('mst_ledger_entries')->insert([
|
||||
'store_id' => $storeId, 'voucher_no' => $voucherNo, 'voucher_type' => 'OPENING_STOCK', 'voucher_date' => date('Y-m-d'),
|
||||
'account_head_id' => $equityHead['head_id'], 'debit_amount' => 0, 'credit_amount' => $totalValuation,
|
||||
'narration' => "Marg ERP Opening Capital Credit"
|
||||
'narration' => "Excel/CSV Opening Capital Credit"
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
@ -440,7 +440,7 @@ class MedicalStoreAdmin extends BaseController
|
|||
|
||||
return $this->response->setJSON([
|
||||
'ok' => true,
|
||||
'message' => "Successfully imported {$importedRows} inventory lines from Marg Pharmacy file.",
|
||||
'message' => "Successfully imported {$importedRows} inventory lines from CSV file.",
|
||||
'details' => [
|
||||
'imported_rows' => $importedRows,
|
||||
'new_catalog_items'=> $newItems,
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
<small class="text-muted">Manage hospital building stores, Indian Drug Licenses (20B/21B), GSTIN, direct URLs, and machine terminal security</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-success btn-sm" id="btn_open_marg_import">
|
||||
<i class="bi bi-file-earmark-arrow-up me-1"></i> Marg ERP / Excel Import
|
||||
<button type="button" class="btn btn-outline-success btn-sm" id="btn_open_excel_import">
|
||||
<i class="bi bi-file-earmark-arrow-up me-1"></i> Excel / CSV Import
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="btn_add_store">
|
||||
<i class="bi bi-plus-lg me-1"></i> Add Medical Store
|
||||
|
|
@ -414,23 +414,23 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal 3: Marg ERP / Excel Inventory Import -->
|
||||
<div class="modal fade" id="margImportModal" tabindex="-1" aria-hidden="true">
|
||||
<!-- Modal 3: Excel / CSV Inventory Import -->
|
||||
<div class="modal fade" id="excelImportModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<div class="modal-header bg-success text-white py-2.5">
|
||||
<h6 class="modal-title fw-bold"><i class="bi bi-file-earmark-arrow-up me-1"></i> Marg ERP / Excel Inventory Import</h6>
|
||||
<h6 class="modal-title fw-bold"><i class="bi bi-file-earmark-arrow-up me-1"></i> Excel / CSV Inventory Import</h6>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form id="margImportForm" enctype="multipart/form-data">
|
||||
<form id="excelImportForm" enctype="multipart/form-data">
|
||||
<div class="modal-body p-3">
|
||||
<p class="small text-muted mb-3">
|
||||
Quickly migrate stock from <strong>Marg Pharmacy Software</strong> or Microsoft Excel into the store's shared master catalog and batch stock.
|
||||
Quickly migrate stock from Microsoft Excel or CSV files into the store's shared master catalog and batch stock.
|
||||
</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold mb-1">Target Medical Store Counter <span class="text-danger">*</span></label>
|
||||
<select class="form-select form-select-sm" name="store_id" id="marg_store_id" required>
|
||||
<select class="form-select form-select-sm" name="store_id" id="excel_store_id" required>
|
||||
<?php foreach ($stores as $st): ?>
|
||||
<option value="<?= (int)$st['store_id'] ?>"><?= esc($st['store_name']) ?> (<?= esc($st['building_name'] ?: 'Building') ?>)</option>
|
||||
<?php endforeach; ?>
|
||||
|
|
@ -439,7 +439,7 @@
|
|||
|
||||
<div class="mb-3">
|
||||
<label class="form-label small fw-bold mb-1">Upload CSV / Excel Export <span class="text-danger">*</span></label>
|
||||
<input type="file" class="form-control form-control-sm" name="import_file" id="marg_file" accept=".csv, .txt" required>
|
||||
<input type="file" class="form-control form-control-sm" name="import_file" id="excel_file" accept=".csv, .txt" required>
|
||||
<small class="text-muted" style="font-size: 11px;">
|
||||
Supported columns: Item Name, Generic, Batch No, Expiry (MM/YY), MRP, PTR/Cost, HSN, GST%, Opening Qty.
|
||||
</small>
|
||||
|
|
@ -447,12 +447,12 @@
|
|||
|
||||
<div class="alert alert-light border py-2 mb-0" style="font-size: 11px;">
|
||||
<i class="bi bi-download text-primary me-1"></i>
|
||||
<a href="javascript:downloadSampleMargCsv()" class="text-decoration-none fw-bold">Download Sample Marg CSV Template</a>
|
||||
<a href="javascript:downloadSampleCsv()" class="text-decoration-none fw-bold">Download Sample CSV Template</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer py-2 bg-light">
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-success btn-sm" id="btn_submit_marg">
|
||||
<button type="submit" class="btn btn-success btn-sm" id="btn_submit_excel">
|
||||
<i class="bi bi-upload me-1"></i> Start Import
|
||||
</button>
|
||||
</div>
|
||||
|
|
@ -528,8 +528,8 @@ document.getElementById('btn_add_store').addEventListener('click', function() {
|
|||
modal.show();
|
||||
});
|
||||
|
||||
document.getElementById('btn_open_marg_import').addEventListener('click', function() {
|
||||
var modal = new bootstrap.Modal(document.getElementById('margImportModal'));
|
||||
document.getElementById('btn_open_excel_import').addEventListener('click', function() {
|
||||
var modal = new bootstrap.Modal(document.getElementById('excelImportModal'));
|
||||
modal.show();
|
||||
});
|
||||
|
||||
|
|
@ -627,15 +627,15 @@ function deleteStore(storeId, storeName) {
|
|||
});
|
||||
}
|
||||
|
||||
// Marg CSV Import Submit
|
||||
$('#margImportForm').on('submit', function(e) {
|
||||
// Excel / CSV Import Submit
|
||||
$('#excelImportForm').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
var formData = new FormData(this);
|
||||
var btn = $('#btn_submit_marg');
|
||||
var btn = $('#btn_submit_excel');
|
||||
btn.prop('disabled', true).text('Importing records...');
|
||||
|
||||
$.ajax({
|
||||
url: '<?= base_url('setting/admin/medical-store/import-marg') ?>',
|
||||
url: '<?= base_url('setting/admin/medical-store/import-excel') ?>',
|
||||
method: 'POST',
|
||||
data: formData,
|
||||
contentType: false,
|
||||
|
|
@ -657,7 +657,7 @@ $('#margImportForm').on('submit', function(e) {
|
|||
});
|
||||
});
|
||||
|
||||
function downloadSampleMargCsv() {
|
||||
function downloadSampleCsv() {
|
||||
var csvContent = "Item Name,Generic Name,Category,Packing,Batch No,Expiry Date,MRP,PTR,HSN Code,GST Rate,Opening Qty,Barcode\n" +
|
||||
"Augmentin 625 Duo Tablet,Amoxycillin + Clavulanic Acid,Tablet,10 Tab,AUG-991,10/27,204.50,155.00,3004,12,100,890103000001\n" +
|
||||
"Pan 40 Tablet,Pantoprazole 40mg,Tablet,15 Tab,PAN-442,04/27,162.00,115.00,3004,12,150,890103000002\n" +
|
||||
|
|
@ -668,7 +668,7 @@ function downloadSampleMargCsv() {
|
|||
var link = document.createElement("a");
|
||||
var url = URL.createObjectURL(blob);
|
||||
link.setAttribute("href", url);
|
||||
link.setAttribute("download", "Marg_Pharmacy_Sample_Import.csv");
|
||||
link.setAttribute("download", "Pharmacy_Sample_Stock_Import.csv");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
|
|
|||
|
|
@ -677,7 +677,7 @@ function App() {
|
|||
<i className="bi bi-boxes me-1"></i> Live Stock & Batches
|
||||
</button>
|
||||
<button className={`nav-link ${activeTab === 'purchase' ? 'active' : ''}`} onClick={() => setActiveTab('purchase')}>
|
||||
<i className="bi bi-box-arrow-in-down me-1"></i> Purchases & Marg Import
|
||||
<i className="bi bi-box-arrow-in-down me-1"></i> Purchases & Stock Import
|
||||
</button>
|
||||
<button className={`nav-link ${activeTab === 'transfer' ? 'active' : ''}`} onClick={() => setActiveTab('transfer')}>
|
||||
<i className="bi bi-arrow-left-right me-1"></i> Inter-Store Indents
|
||||
|
|
@ -1532,7 +1532,7 @@ function PosCounter({ store, notify }) {
|
|||
}
|
||||
};
|
||||
|
||||
// Global Keyboard Shortcuts (Marg ERP / Desktop Pharmacy Standard)
|
||||
// Global Keyboard Shortcuts (Desktop Pharmacy Standard)
|
||||
useEffect(() => {
|
||||
const handleGlobalKeyDown = (e) => {
|
||||
// Dismiss Modals on Escape
|
||||
|
|
@ -3495,7 +3495,7 @@ function PosCounter({ store, notify }) {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Sales Return & Same-Invoice Exchange Modal (B2C Marg ERP Standard) */}
|
||||
{/* Sales Return & Same-Invoice Exchange Modal (B2C Retail Standard) */}
|
||||
{showReturnModal && (
|
||||
<div className="modal fade show d-block" style={{ backgroundColor: 'rgba(15, 23, 42, 0.75)' }} tabIndex="-1">
|
||||
<div className="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
|
|
@ -3506,7 +3506,7 @@ function PosCounter({ store, notify }) {
|
|||
<div>
|
||||
<h6 className="modal-title mb-0 fw-bold">Sales Return & Same-Invoice Exchange Desk</h6>
|
||||
<small className="text-secondary" style={{ fontSize: '0.75rem' }}>
|
||||
B2C Retail Returns • Stock Replenishment • GST Credit Note • Marg ERP Standard (F6 / Alt+R)
|
||||
B2C Retail Returns • Stock Replenishment • GST Credit Note • Fast POS Standard (F6 / Alt+R)
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -4011,11 +4011,11 @@ function InventoryView({ store, notify }) {
|
|||
}
|
||||
|
||||
// =========================================================================
|
||||
// 3. INWARD PURCHASES, SUPPLIERS & MARG ERP IMPORT
|
||||
// 3. INWARD PURCHASES, SUPPLIERS & EXCEL / CSV IMPORT
|
||||
// =========================================================================
|
||||
function PurchaseView({ store, notify }) {
|
||||
if (!store) return null;
|
||||
const [subTab, setSubTab] = useState('inward'); // inward, marg, suppliers
|
||||
const [subTab, setSubTab] = useState('inward'); // inward, excel, suppliers
|
||||
const [suppliers, setSuppliers] = useState([]);
|
||||
const [selectedSupplierId, setSelectedSupplierId] = useState('');
|
||||
const [invoiceNo, setInvoiceNo] = useState('');
|
||||
|
|
@ -4759,8 +4759,8 @@ function PurchaseView({ store, notify }) {
|
|||
<button className={`btn btn-sm ${subTab === 'suppliers' ? 'btn-primary shadow-sm' : 'btn-outline-secondary'}`} onClick={() => setSubTab('suppliers')}>
|
||||
<i className="bi bi-truck me-1"></i> Supplier Directory ({suppliers.length})
|
||||
</button>
|
||||
<button className={`btn btn-sm ${subTab === 'marg' ? 'btn-success shadow-sm' : 'btn-outline-secondary'}`} onClick={() => setSubTab('marg')}>
|
||||
<i className="bi bi-file-earmark-spreadsheet me-1"></i> Marg ERP / Excel Import
|
||||
<button className={`btn btn-sm ${subTab === 'excel' ? 'btn-success shadow-sm' : 'btn-outline-secondary'}`} onClick={() => setSubTab('excel')}>
|
||||
<i className="bi bi-file-earmark-spreadsheet me-1"></i> Excel / CSV Import
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
|
@ -5035,22 +5035,22 @@ function PurchaseView({ store, notify }) {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{subTab === 'marg' && (
|
||||
{subTab === 'excel' && (
|
||||
<div className="counter-card">
|
||||
<h5 className="fw-bold text-success mb-2"><i className="bi bi-file-earmark-spreadsheet me-1"></i> Marg ERP / Excel Inventory Importer</h5>
|
||||
<h5 className="fw-bold text-success mb-2"><i className="bi bi-file-earmark-spreadsheet me-1"></i> Excel / CSV Inventory Importer</h5>
|
||||
<p className="text-muted small mb-3">
|
||||
Upload your stock export from <strong>Marg Pharmacy Software</strong> or Microsoft Excel to seamlessly import medicines, batch numbers, MRP, PTR, HSN codes, and opening stock into <strong>{store.store_name}</strong>.
|
||||
Upload your stock export from Microsoft Excel or CSV format to seamlessly import medicines, batch numbers, MRP, PTR, HSN codes, and opening stock into <strong>{store.store_name}</strong>.
|
||||
</p>
|
||||
|
||||
<div className="p-4 bg-light rounded-3 border text-center mb-3">
|
||||
<i className="bi bi-cloud-arrow-up fs-1 text-success d-block mb-2"></i>
|
||||
<h6 className="fw-bold">Upload Marg CSV / Excel File</h6>
|
||||
<p className="small text-muted mb-3">Ensure file is exported in CSV format from Marg ERP.</p>
|
||||
<input type="file" id="marg_file_input" accept=".csv, .txt" className="form-control form-control-sm w-50 mx-auto mb-3" />
|
||||
<h6 className="fw-bold">Upload CSV / Excel File</h6>
|
||||
<p className="small text-muted mb-3">Ensure file is formatted as standard CSV or Excel-exported CSV.</p>
|
||||
<input type="file" id="excel_file_input" accept=".csv, .txt" className="form-control form-control-sm w-50 mx-auto mb-3" />
|
||||
<button
|
||||
className="btn btn-success-custom"
|
||||
onClick={async () => {
|
||||
const fileInput = document.getElementById('marg_file_input');
|
||||
const fileInput = document.getElementById('excel_file_input');
|
||||
if (!fileInput.files || fileInput.files.length === 0) {
|
||||
alert('Please select a CSV file first.');
|
||||
return;
|
||||
|
|
@ -5060,7 +5060,7 @@ function PurchaseView({ store, notify }) {
|
|||
formData.append('import_file', fileInput.files[0]);
|
||||
|
||||
try {
|
||||
const res = await fetch(API_BASE + 'stock/import-marg', {
|
||||
const res = await fetch(API_BASE + 'stock/import-excel', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -1,187 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Test Terminal Verification and Marg ERP CSV Import
|
||||
*/
|
||||
|
||||
$db = new mysqli('127.0.0.1', 'root', '', 'hms_data_ci4', 3306);
|
||||
if ($db->connect_error) {
|
||||
die("DB Connection failed: " . $db->connect_error . "\n");
|
||||
}
|
||||
|
||||
echo "=== 1. TESTING TERMINAL DEVICE VERIFICATION VIA SECURITY KEY ===\n";
|
||||
|
||||
// Get store 1
|
||||
$storeRes = $db->query("SELECT * FROM mst_stores WHERE store_id = 1");
|
||||
$store1 = $storeRes->fetch_assoc();
|
||||
echo " - Store: {$store1['store_name']} (Slug: {$store1['store_slug']}, Key: {$store1['security_key']})\n";
|
||||
|
||||
// Verify with Security Key
|
||||
$machineName = "OPD-Counter-Dell-01";
|
||||
$fingerprint = "fp_" . md5("screen_1920x1080_win64_dell");
|
||||
$token = bin2hex(random_bytes(32));
|
||||
|
||||
$db->query("
|
||||
INSERT INTO `mst_store_devices` (
|
||||
`store_id`, `machine_name`, `device_token`, `device_fingerprint`,
|
||||
`ip_address`, `user_agent`, `verified_by_method`, `status`
|
||||
) VALUES (
|
||||
1, '$machineName', '$token', '$fingerprint',
|
||||
'192.168.1.101', 'Mozilla/5.0 (Windows NT 10.0; Win64)', 'SECURITY_KEY', 'authorized'
|
||||
)
|
||||
");
|
||||
$deviceId = $db->insert_id;
|
||||
echo " ✓ Machine '{$machineName}' registered (Device ID: {$deviceId}, Token: " . substr($token, 0, 16) . "...)\n";
|
||||
|
||||
// Verify status check
|
||||
$chk = $db->query("SELECT * FROM mst_store_devices WHERE device_token = '$token' AND status = 'authorized'")->fetch_assoc();
|
||||
if ($chk) {
|
||||
echo " ✓ Device check passed: Machine is ACTIVE & AUTHORIZED for Store {$chk['store_id']}\n";
|
||||
} else {
|
||||
echo " ✗ Device check failed!\n";
|
||||
}
|
||||
|
||||
echo "\n=== 2. TESTING OTP GENERATION & MACHINE VERIFICATION ===\n";
|
||||
|
||||
// Generate OTP for store 2
|
||||
$store2Res = $db->query("SELECT * FROM mst_stores WHERE store_id = 2");
|
||||
$store2 = $store2Res->fetch_assoc();
|
||||
|
||||
$testOtp = (string)rand(100000, 999999);
|
||||
$testExpiry = date('Y-m-d H:i:s', strtotime('+60 minutes'));
|
||||
$db->query("UPDATE mst_stores SET current_otp = '$testOtp', otp_expiry = '$testExpiry' WHERE store_id = 2");
|
||||
echo " - Store: {$store2['store_name']} | Generated 60-min OTP: $testOtp (Expires: $testExpiry)\n";
|
||||
|
||||
// Machine verifies with OTP
|
||||
$token2 = bin2hex(random_bytes(32));
|
||||
$db->query("
|
||||
INSERT INTO `mst_store_devices` (
|
||||
`store_id`, `machine_name`, `device_token`, `device_fingerprint`,
|
||||
`ip_address`, `user_agent`, `verified_by_method`, `status`
|
||||
) VALUES (
|
||||
2, 'IPD-Counter-HP-02', '$token2', 'fp_hp_terminal_02',
|
||||
'192.168.1.102', 'Mozilla/5.0 (Windows NT 10.0)', 'OTP', 'authorized'
|
||||
)
|
||||
");
|
||||
$dev2Id = $db->insert_id;
|
||||
echo " ✓ Machine 'IPD-Counter-HP-02' authorized using OTP (Device ID: {$dev2Id})\n";
|
||||
|
||||
// Revoke machine terminal by Admin
|
||||
$db->query("UPDATE mst_store_devices SET status = 'revoked' WHERE device_id = $dev2Id");
|
||||
$chkRevoked = $db->query("SELECT * FROM mst_store_devices WHERE device_token = '$token2' AND status = 'authorized'")->fetch_assoc();
|
||||
if (!$chkRevoked) {
|
||||
echo " ✓ Admin Revocation confirmed: Device token rejected when revoked.\n";
|
||||
} else {
|
||||
echo " ✗ Revocation check failed!\n";
|
||||
}
|
||||
|
||||
echo "\n=== 3. TESTING MARG ERP CSV IMPORT SIMULATION ===\n";
|
||||
|
||||
$csvPath = __DIR__ . '/test_marg_sample.csv';
|
||||
$handle = fopen($csvPath, 'r');
|
||||
$header = fgetcsv($handle);
|
||||
echo " - CSV Header: " . implode(', ', $header) . "\n";
|
||||
|
||||
$imported = 0;
|
||||
while (($row = fgetcsv($handle)) !== false) {
|
||||
$itemName = trim($row[0]);
|
||||
$pack = trim($row[1]);
|
||||
$batchNo = trim($row[2]);
|
||||
$expStr = trim($row[3]);
|
||||
$mrp = (float)$row[4];
|
||||
$ptr = (float)$row[5];
|
||||
$hsn = trim($row[6]);
|
||||
$gst = (float)$row[7];
|
||||
$qty = (int)$row[8];
|
||||
|
||||
// Format Expiry MM/YYYY -> YYYY-MM-lastday
|
||||
$parts = explode('/', $expStr);
|
||||
$expDate = date('Y-m-t', strtotime("{$parts[1]}-{$parts[0]}-01"));
|
||||
|
||||
// Check/create master drug catalog
|
||||
$findItem = $db->query("SELECT item_id FROM mst_items WHERE item_name = '" . $db->real_escape_string($itemName) . "'")->fetch_assoc();
|
||||
if ($findItem) {
|
||||
$itemId = $findItem['item_id'];
|
||||
} else {
|
||||
$db->query("
|
||||
INSERT INTO `mst_items` (
|
||||
`item_name`, `unit_pack`, `hsn_code`, `gst_rate`, `category`, `is_active`
|
||||
) VALUES (
|
||||
'" . $db->real_escape_string($itemName) . "', '$pack', '$hsn', $gst, 'Allopathy', 1
|
||||
)
|
||||
");
|
||||
$itemId = $db->insert_id;
|
||||
}
|
||||
|
||||
// Check/create batch for Store 1
|
||||
$findBatch = $db->query("SELECT batch_id FROM mst_batches WHERE store_id = 1 AND item_id = $itemId AND batch_no = '$batchNo'")->fetch_assoc();
|
||||
if ($findBatch) {
|
||||
$batchId = $findBatch['batch_id'];
|
||||
} else {
|
||||
$db->query("
|
||||
INSERT INTO `mst_batches` (
|
||||
`store_id`, `item_id`, `batch_no`, `expiry_date`, `ptr`, `mrp`, `gst_rate`
|
||||
) VALUES (
|
||||
1, $itemId, '$batchNo', '$expDate', $ptr, $mrp, $gst
|
||||
)
|
||||
");
|
||||
$batchId = $db->insert_id;
|
||||
}
|
||||
|
||||
// Update or insert stock
|
||||
$stockRow = $db->query("SELECT stock_id, current_qty FROM mst_stock WHERE store_id = 1 AND batch_id = $batchId")->fetch_assoc();
|
||||
if ($stockRow) {
|
||||
$newQty = $stockRow['current_qty'] + $qty;
|
||||
$db->query("UPDATE mst_stock SET current_qty = $newQty WHERE stock_id = {$stockRow['stock_id']}");
|
||||
} else {
|
||||
$db->query("
|
||||
INSERT INTO `mst_stock` (
|
||||
`store_id`, `item_id`, `batch_id`, `current_qty`
|
||||
) VALUES (
|
||||
1, $itemId, $batchId, $qty
|
||||
)
|
||||
");
|
||||
}
|
||||
|
||||
// Audit log
|
||||
$db->query("
|
||||
INSERT INTO `mst_stock_audit` (
|
||||
`store_id`, `item_id`, `batch_id`, `audit_type`, `system_qty`, `physical_qty`,
|
||||
`variation_qty`, `rate`, `total_value`, `remarks`
|
||||
) VALUES (
|
||||
1, $itemId, $batchId, 'OPENING_STOCK', 0, $qty,
|
||||
$qty, $ptr, " . ($ptr * $qty) . ", 'Imported via Marg ERP CSV'
|
||||
)
|
||||
");
|
||||
|
||||
$imported++;
|
||||
echo " ✓ Imported '{$itemName}' | Batch: {$batchNo} | Exp: {$expDate} | MRP: ₹{$mrp} | Qty: {$qty}\n";
|
||||
}
|
||||
fclose($handle);
|
||||
|
||||
echo "\n - Total rows imported from Marg format: $imported\n";
|
||||
|
||||
echo "\n=== 4. VERIFYING ISOLATION OF STOCKS ACROSS STORES ===\n";
|
||||
|
||||
// Check that Store 2 does NOT see Store 1's imported stock
|
||||
$st2Check = $db->query("
|
||||
SELECT i.item_name, s.current_qty
|
||||
FROM mst_stock s
|
||||
JOIN mst_items i ON i.item_id = s.item_id
|
||||
WHERE s.store_id = 2 AND i.item_name = 'Azithral 500 Tablet'
|
||||
")->fetch_assoc();
|
||||
|
||||
if (!$st2Check) {
|
||||
echo " ✓ Store Isolation Verified: 'Azithral 500 Tablet' has 0 stock in Store 2 (OPD Counter)\n";
|
||||
} else {
|
||||
echo " - Store 2 stock: " . ($st2Check['current_qty'] ?? 0) . "\n";
|
||||
}
|
||||
|
||||
$st1Check = $db->query("
|
||||
SELECT i.item_name, s.current_qty
|
||||
FROM mst_stock s
|
||||
JOIN mst_items i ON i.item_id = s.item_id
|
||||
WHERE s.store_id = 1 AND i.item_name = 'Azithral 500 Tablet'
|
||||
")->fetch_assoc();
|
||||
echo " ✓ Store 1 stock: '{$st1Check['item_name']}' has {$st1Check['current_qty']} units in Store 1 (Main Store)\n";
|
||||
|
||||
echo "\n=== ALL VERIFICATIONS PASSED SUCCESSFULLY ===\n";
|
||||
|
|
@ -1,6 +0,0 @@
|
|||
Item Name,Packing,Batch,Exp Date,MRP,PTR,HSN,GST%,Qty
|
||||
Azithral 500 Tablet,1x5,AZI-901,12/2026,125.00,92.50,3004,12,50
|
||||
Pantocid DSR Capsule,1x10,PNT-441,08/2027,210.00,155.00,3004,12,100
|
||||
Paracetamol 650 Tablet,1x15,PCM-102,05/2027,32.50,22.00,3004,12,200
|
||||
Betadine 10% Ointment 20gm,1x1,BT-882,03/2028,140.00,105.00,3004,12,30
|
||||
Monocef 1gm Injection,1 Vial,MNC-774,11/2026,68.00,51.00,3004,12,80
|
||||
|
Loading…
Reference in New Issue
Block a user