diff --git a/app/Config/Routes.php b/app/Config/Routes.php index 1a1c393..9618a41 100644 --- a/app/Config/Routes.php +++ b/app/Config/Routes.php @@ -1671,6 +1671,7 @@ $routes->group('api/v1', ['namespace' => 'App\Controllers\Api\v1'], static funct $routes->get('medical-store/sales/recent', 'MedicalStoreApi::recentSales'); $routes->get('medical-store/suppliers', 'MedicalStoreApi::suppliers'); $routes->post('medical-store/suppliers/save', 'MedicalStoreApi::saveSupplier'); + $routes->post('medical-store/suppliers/delete', 'MedicalStoreApi::deleteSupplier'); $routes->post('medical-store/purchase/save', 'MedicalStoreApi::savePurchase'); $routes->post('medical-store/purchase/payment', 'MedicalStoreApi::saveSupplierPayment'); $routes->post('medical-store/transfer/request', 'MedicalStoreApi::requestTransfer'); diff --git a/app/Controllers/Api/v1/MedicalStoreApi.php b/app/Controllers/Api/v1/MedicalStoreApi.php index bd0693b..ac09a35 100644 --- a/app/Controllers/Api/v1/MedicalStoreApi.php +++ b/app/Controllers/Api/v1/MedicalStoreApi.php @@ -1641,6 +1641,25 @@ class MedicalStoreApi extends BaseController return $this->response->setJSON(['status' => 1, 'message' => 'Supplier saved successfully.', 'supplier_id' => $id]); } + public function deleteSupplier() + { + $json = $this->request->getJSON(true) ?: $this->request->getPost(); + $id = (int)($json['supplier_id'] ?? 0); + if ($id <= 0) { + return $this->response->setStatusCode(400)->setJSON(['status' => 0, 'message' => 'Invalid supplier ID.']); + } + + // Check if supplier is referenced in purchases + $hasPurchases = $this->db->table('mst_purchases')->where('supplier_id', $id)->countAllResults(); + if ($hasPurchases > 0) { + $this->db->table('mst_suppliers')->where('supplier_id', $id)->update(['is_active' => 0]); + return $this->response->setJSON(['status' => 1, 'message' => 'Supplier deactivated (cannot delete because purchase bills are linked).']); + } + + $this->db->table('mst_suppliers')->where('supplier_id', $id)->delete(); + return $this->response->setJSON(['status' => 1, 'message' => 'Supplier removed successfully.']); + } + public function savePurchase() { $json = $this->request->getJSON(true) ?: $this->request->getPost(); diff --git a/public/App/MedicalStore/index.html b/public/App/MedicalStore/index.html index e1bdbf3..a6c671c 100644 --- a/public/App/MedicalStore/index.html +++ b/public/App/MedicalStore/index.html @@ -4021,6 +4021,26 @@ function PurchaseView({ store, notify }) { const [invoiceNo, setInvoiceNo] = useState(''); const [invoiceDate, setInvoiceDate] = useState(new Date().toISOString().split('T')[0]); + // Supplier Add/Edit Modal State + const [showSupplierModal, setShowSupplierModal] = useState(false); + const [supplierSearch, setSupplierSearch] = useState(''); + const [isSavingSupplier, setIsSavingSupplier] = useState(false); + const [supplierForm, setSupplierForm] = useState({ + supplier_id: 0, + supplier_name: '', + contact_person: '', + phone: '', + email: '', + gstin: '', + pan_no: '', + dl_no_20b: '', + dl_no_21b: '', + state_code: '07', + credit_days: 30, + address: '', + bank_details: '' + }); + const [rows, setRows] = useState([ { item_name: '', generic_name: '', batch_no: '', expiry_date: '', qty_packs: 1, free_qty_packs: 0, units_per_pack: 10, mrp: 0, ptr: 0, discount_pct: 0, gst_rate: 12 } ]); @@ -4039,6 +4059,107 @@ function PurchaseView({ store, notify }) { } }; + const openAddSupplier = () => { + setSupplierForm({ + supplier_id: 0, + supplier_name: '', + contact_person: '', + phone: '', + email: '', + gstin: '', + pan_no: '', + dl_no_20b: '', + dl_no_21b: '', + state_code: '07', + credit_days: 30, + address: '', + bank_details: '' + }); + setShowSupplierModal(true); + }; + + const openEditSupplier = (s) => { + setSupplierForm({ + supplier_id: s.supplier_id, + supplier_name: s.supplier_name || '', + contact_person: s.contact_person || '', + phone: s.phone || '', + email: s.email || '', + gstin: s.gstin || '', + pan_no: s.pan_no || '', + dl_no_20b: s.dl_no_20b || '', + dl_no_21b: s.dl_no_21b || '', + state_code: s.state_code || '07', + credit_days: s.credit_days || 30, + address: s.address || '', + bank_details: s.bank_details || '' + }); + setShowSupplierModal(true); + }; + + const handleSaveSupplier = async (e) => { + if (e) e.preventDefault(); + if (!supplierForm.supplier_name.trim()) { + notify('Supplier / Distributor name is required.', 'error'); + return; + } + + setIsSavingSupplier(true); + try { + const res = await fetch(API_BASE + 'suppliers/save', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(supplierForm) + }); + const data = await res.json(); + if (data.status) { + notify(supplierForm.supplier_id ? 'Supplier updated successfully!' : 'New supplier added successfully!', 'success'); + setShowSupplierModal(false); + await fetchSuppliers(); + if (data.supplier_id) { + setSelectedSupplierId(String(data.supplier_id)); + } + } else { + notify(data.message || 'Failed to save supplier.', 'error'); + } + } catch (err) { + notify('Network error saving supplier: ' + err.message, 'error'); + } finally { + setIsSavingSupplier(false); + } + }; + + const handleDeleteSupplier = async (supplierId, supplierName) => { + if (!confirm(`Are you sure you want to remove / deactivate supplier "${supplierName}"?`)) return; + try { + const res = await fetch(API_BASE + 'suppliers/delete', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ supplier_id: supplierId }) + }); + const data = await res.json(); + if (data.status) { + notify(data.message || 'Supplier removed successfully.', 'success'); + await fetchSuppliers(); + } else { + notify(data.message || 'Failed to remove supplier.', 'error'); + } + } catch (err) { + notify('Error: ' + err.message, 'error'); + } + }; + + const filteredSuppliers = suppliers.filter(s => { + if (!supplierSearch.trim()) return true; + const q = supplierSearch.toLowerCase(); + return (s.supplier_name && s.supplier_name.toLowerCase().includes(q)) || + (s.gstin && s.gstin.toLowerCase().includes(q)) || + (s.dl_no_20b && s.dl_no_20b.toLowerCase().includes(q)) || + (s.dl_no_21b && s.dl_no_21b.toLowerCase().includes(q)) || + (s.phone && s.phone.toLowerCase().includes(q)) || + (s.contact_person && s.contact_person.toLowerCase().includes(q)); + }); + const addRow = () => { setRows([...rows, { item_name: '', generic_name: '', batch_no: '', expiry_date: '', qty_packs: 1, free_qty_packs: 0, units_per_pack: 10, mrp: 0, ptr: 0, discount_pct: 0, gst_rate: 12 }]); }; @@ -4122,7 +4243,7 @@ function PurchaseView({ store, notify }) { Marg ERP / Excel Import @@ -4131,7 +4252,12 @@ function PurchaseView({ store, notify }) {
Distributor Purchase Inward (Stock Entry)
- +
+ + +
setSupplierSearch(e.target.value)} + /> + {supplierSearch && ( + + )} +
+ +
+ +
- +
- - + + - + + + - {suppliers.map((s, i) => ( - - - - - - - + {filteredSuppliers.length === 0 ? ( + + - ))} + ) : ( + filteredSuppliers.map((s, i) => ( + + + + + + + + + + + )) + )}
#Supplier Name#Distributor Name DL Form 20B / 21BGSTINGSTIN & PAN Phone / Contact Credit TermsAddress / BankActions
{i + 1}{s.supplier_name}{s.dl_no_20b || 'N/A'} / {s.dl_no_21b || 'N/A'}{s.gstin}{s.phone} ({s.contact_person}){s.credit_days} Days
+ + {suppliers.length === 0 + ? 'No distributors registered yet. Click "Add New Supplier" to create one.' + : 'No suppliers match your search query.'} +
{i + 1} +
{s.supplier_name}
+ {s.email && {s.email}} +
+
20B: {s.dl_no_20b || N/A}
+
21B: {s.dl_no_21b || N/A}
+
+
{s.gstin || 'N/A'}
+ {s.pan_no && PAN: {s.pan_no}} +
+
{s.phone || 'N/A'}
+ {s.contact_person && {s.contact_person}} +
+ + {s.credit_days || 30} Days + + + {s.address &&
{s.address}
} + {s.bank_details &&
{s.bank_details}
} + {!s.address && !s.bank_details && -} +
+
+ + +
+
)} + + {/* Supplier Add / Edit Modal */} + {showSupplierModal && ( +
+
+
+
+
+
+ +
+
+
+ {supplierForm.supplier_id ? 'Edit Supplier / Distributor Details' : 'Add New Drug Distributor & Supplier'} +
+ Statutory Drug Licenses, GSTIN, contact & credit terms +
+
+ +
+ +
+
+
+
+ Basic Distributor Information +
+
+
+ + setSupplierForm({ ...supplierForm, supplier_name: e.target.value })} + required + autoFocus + /> +
+
+ + setSupplierForm({ ...supplierForm, contact_person: e.target.value })} + /> +
+
+ + setSupplierForm({ ...supplierForm, phone: e.target.value })} + /> +
+
+ + setSupplierForm({ ...supplierForm, email: e.target.value })} + /> +
+
+ +
+ setSupplierForm({ ...supplierForm, credit_days: e.target.value })} + /> + Days +
+
+
+
+ +
+
+ Indian Statutory Drug Licenses & GSTIN +
+
+
+ + setSupplierForm({ ...supplierForm, dl_no_20b: e.target.value })} + /> +
+
+ + setSupplierForm({ ...supplierForm, dl_no_21b: e.target.value })} + /> +
+
+ + setSupplierForm({ ...supplierForm, gstin: e.target.value.toUpperCase() })} + /> +
+
+ + setSupplierForm({ ...supplierForm, pan_no: e.target.value.toUpperCase() })} + /> +
+
+ + setSupplierForm({ ...supplierForm, state_code: e.target.value })} + /> +
+
+
+ +
+
+ Address & Banking Details +
+
+
+ + +
+
+ + +
+
+
+
+ +
+ + +
+
+
+
+
+ )} ); }