document number sequence
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
session_start();
|
||||
require_once '../../../assets/utils/db_auth.php';
|
||||
require_once '../../../assets/utils/classes/DocumentNumberManager.php';
|
||||
|
||||
$mgr = new DocumentNumberManager($pdo2, $company_id);
|
||||
$action = $data['action'] ?? '';
|
||||
|
||||
try {
|
||||
switch ($action) {
|
||||
|
||||
case 'list':
|
||||
$answer['data'] = $mgr->getAllConfigs();
|
||||
$answer['success'] = 1;
|
||||
break;
|
||||
|
||||
case 'options':
|
||||
$key = trim((string)($data['doc_type_key'] ?? ''));
|
||||
if ($key === '') throw new Exception('doc_type_key is required.');
|
||||
$answer['data'] = $mgr->getConfigsForType($key);
|
||||
$answer['success'] = 1;
|
||||
break;
|
||||
|
||||
case 'save':
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
$id = isset($data['id']) && (int)$data['id'] > 0 ? (int)$data['id'] : null;
|
||||
$new_id = $mgr->saveConfig($data, $id);
|
||||
$answer['id'] = $new_id;
|
||||
$answer['message'] = $id ? 'Configuration updated.' : 'Configuration saved.';
|
||||
$answer['success'] = 1;
|
||||
break;
|
||||
|
||||
case 'delete':
|
||||
require_role($user_role, ['owner', 'admin']);
|
||||
$mgr->deleteConfig((int)($data['id'] ?? 0));
|
||||
$answer['message'] = 'Configuration deleted.';
|
||||
$answer['success'] = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
$answer['message'] = 'Unknown action.';
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,254 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../config.php';
|
||||
require '../include_header.php';
|
||||
?>
|
||||
|
||||
<body>
|
||||
<?php require '../include_topbar.php'; ?>
|
||||
<?php require '../include_setting_sidebar.php'; ?>
|
||||
|
||||
<main id="content" class="content py-15">
|
||||
<div class="container-fluid">
|
||||
|
||||
<!-- Page header -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-6 gap-3">
|
||||
<div>
|
||||
<h1 class="fs-3 mb-1">Document Numbering</h1>
|
||||
<p class="mb-0">Configure prefix, format, and sequence digits for each document type</p>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn btn-primary" onclick="open_add_modal()">
|
||||
<i class="ti ti-plus me-1"></i>Add Prefix
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Table -->
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0" id="dt_table">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Document Type</th>
|
||||
<th>Prefix</th>
|
||||
<th>Format</th>
|
||||
<th>Digits</th>
|
||||
<th>Manual Entry</th>
|
||||
<th class="text-end">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dt_tbody">
|
||||
<tr><td colspan="6" class="text-center py-4 text-muted">Loading…</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- Add / Edit modal -->
|
||||
<div class="modal fade" id="dt_modal" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="dt_modal_title">Add Prefix</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<input type="hidden" id="dt_id">
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Document Type <span class="text-danger">*</span></label>
|
||||
<select class="form-select" id="dt_key">
|
||||
<option value="quotation">Quotation</option>
|
||||
<option value="sales_order">Sales Order</option>
|
||||
<option value="invoice">Sales Invoice</option>
|
||||
<option value="credit_note">Sales Credit Note</option>
|
||||
<option value="purchase_request">Purchase Request</option>
|
||||
<option value="purchase_order">Purchase Order</option>
|
||||
<option value="purchase_invoice">Purchase Invoice</option>
|
||||
<option value="supplier_credit_note">Supplier Credit Note</option>
|
||||
<option value="receipt">Receipt</option>
|
||||
<option value="receipt_billing">Receipt Billing</option>
|
||||
<option value="payment">Payment</option>
|
||||
<option value="payment_billing">Payment Billing</option>
|
||||
<option value="manual">Manual Journal</option>
|
||||
<option value="return">Customer Return</option>
|
||||
<option value="supplier_return">Supplier Return</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Prefix <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control text-uppercase" id="dt_prefix" maxlength="20" placeholder="e.g. INV">
|
||||
<div class="form-text">Uppercase letters and numbers only. Will be auto-uppercased on save.</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Format</label>
|
||||
<select class="form-select" id="dt_format">
|
||||
<option value="YYMM">YYMM — e.g. INV-2605-00001</option>
|
||||
<option value="YY">YY — e.g. INV-26-00001</option>
|
||||
<option value="YYMMDD">YYMMDD — e.g. INV-260525-00001</option>
|
||||
<option value="none">None — e.g. INV-00001</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Sequence Digits</label>
|
||||
<input type="number" class="form-control" id="dt_digits" min="5" max="20" value="5">
|
||||
<div class="form-text">Minimum 5. Zero-padded running number length.</div>
|
||||
</div>
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="dt_manual" checked>
|
||||
<label class="form-check-label" for="dt_manual">Allow manual number entry</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="button" class="btn btn-primary" onclick="save_config()">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const API = '<?php echo $server_url; ?>setting/api/engine/document_types.php';
|
||||
|
||||
const FORMAT_LABELS = {
|
||||
YY: 'YY',
|
||||
YYMM: 'YYMM',
|
||||
YYMMDD: 'YYMMDD',
|
||||
none: 'None',
|
||||
};
|
||||
|
||||
const KEY_LABELS = {
|
||||
quotation: 'Quotation',
|
||||
sales_order: 'Sales Order',
|
||||
invoice: 'Sales Invoice',
|
||||
credit_note: 'Sales Credit Note',
|
||||
purchase_request: 'Purchase Request',
|
||||
purchase_order: 'Purchase Order',
|
||||
purchase_invoice: 'Purchase Invoice',
|
||||
supplier_credit_note: 'Supplier Credit Note',
|
||||
receipt: 'Receipt',
|
||||
receipt_billing: 'Receipt Billing',
|
||||
payment: 'Payment',
|
||||
payment_billing: 'Payment Billing',
|
||||
manual: 'Manual Journal',
|
||||
return: 'Customer Return',
|
||||
supplier_return: 'Supplier Return',
|
||||
};
|
||||
|
||||
function load_configs() {
|
||||
ajax_request({
|
||||
url: API,
|
||||
autoPrepare: true,
|
||||
action: 'list',
|
||||
onSuccess: function(res) {
|
||||
render_table(res.data || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function render_table(rows) {
|
||||
const tbody = document.getElementById('dt_tbody');
|
||||
if (!rows.length) {
|
||||
tbody.innerHTML = '<tr><td colspan="6" class="text-center py-4 text-muted">No configurations found.</td></tr>';
|
||||
return;
|
||||
}
|
||||
tbody.innerHTML = rows.map(r => `
|
||||
<tr>
|
||||
<td>${KEY_LABELS[r.doc_type_key] || r.doc_type_key}</td>
|
||||
<td><span class="badge bg-secondary">${r.prefix}</span></td>
|
||||
<td>${FORMAT_LABELS[r.format_type] || r.format_type}</td>
|
||||
<td>${r.sequence_digits}</td>
|
||||
<td>${r.allow_manual ? '<span class="badge bg-success-subtle text-success">Yes</span>' : '<span class="badge bg-secondary-subtle text-secondary">No</span>'}</td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-secondary me-1" onclick='open_edit_modal(${JSON.stringify(r)})'>
|
||||
<i class="ti ti-pencil"></i>
|
||||
</button>
|
||||
${r.id ? `<button class="btn btn-sm btn-outline-danger" onclick="delete_config(${r.id})">
|
||||
<i class="ti ti-trash"></i>
|
||||
</button>` : ''}
|
||||
</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
}
|
||||
|
||||
function open_add_modal() {
|
||||
document.getElementById('dt_modal_title').textContent = 'Add Prefix';
|
||||
document.getElementById('dt_id').value = '';
|
||||
document.getElementById('dt_key').value = 'quotation';
|
||||
document.getElementById('dt_key').disabled = false;
|
||||
document.getElementById('dt_prefix').value = '';
|
||||
document.getElementById('dt_format').value = 'YYMM';
|
||||
document.getElementById('dt_digits').value = '5';
|
||||
document.getElementById('dt_manual').checked = true;
|
||||
bootstrap.Modal.getOrCreateInstance(document.getElementById('dt_modal')).show();
|
||||
}
|
||||
|
||||
function open_edit_modal(r) {
|
||||
document.getElementById('dt_modal_title').textContent = 'Edit Prefix';
|
||||
document.getElementById('dt_id').value = r.id || '';
|
||||
document.getElementById('dt_key').value = r.doc_type_key;
|
||||
document.getElementById('dt_key').disabled = true;
|
||||
document.getElementById('dt_prefix').value = r.prefix;
|
||||
document.getElementById('dt_format').value = r.format_type;
|
||||
document.getElementById('dt_digits').value = r.sequence_digits;
|
||||
document.getElementById('dt_manual').checked = r.allow_manual == 1;
|
||||
bootstrap.Modal.getOrCreateInstance(document.getElementById('dt_modal')).show();
|
||||
}
|
||||
|
||||
function save_config() {
|
||||
const id = document.getElementById('dt_id').value;
|
||||
const key = document.getElementById('dt_key').value;
|
||||
const prefix = document.getElementById('dt_prefix').value.trim().toUpperCase();
|
||||
const format = document.getElementById('dt_format').value;
|
||||
const digits = parseInt(document.getElementById('dt_digits').value, 10);
|
||||
const manual = document.getElementById('dt_manual').checked ? 1 : 0;
|
||||
|
||||
if (!prefix) { bootbox.alert('Prefix is required.'); return; }
|
||||
|
||||
ajax_request({
|
||||
url: API,
|
||||
autoPrepare: true,
|
||||
action: 'save',
|
||||
data: { id: id || null, doc_type_key: key, prefix, format_type: format, sequence_digits: digits, allow_manual: manual },
|
||||
onSuccess: function(res) {
|
||||
bootstrap.Modal.getOrCreateInstance(document.getElementById('dt_modal')).hide();
|
||||
bootbox.alert(res.message || 'Saved.');
|
||||
load_configs();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function delete_config(id) {
|
||||
bootbox.confirm('Delete this prefix configuration?', function(ok) {
|
||||
if (!ok) return;
|
||||
ajax_request({
|
||||
url: API,
|
||||
autoPrepare: true,
|
||||
action: 'delete',
|
||||
data: { id },
|
||||
onSuccess: function(res) {
|
||||
bootbox.alert(res.message || 'Deleted.');
|
||||
load_configs();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
load_configs();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user