softDelete features

This commit is contained in:
Thanakorn S
2026-05-22 14:07:22 +07:00
parent 738f600fe8
commit 648efee991
59 changed files with 1783 additions and 16 deletions
+33
View File
@@ -0,0 +1,33 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require_role($user_role, ['owner', 'admin']);
require '../../../assets/utils/classes/InvoiceManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid invoice ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id) {
$mgr = new InvoiceManager($pdo, $company_id);
$mgr->softDelete($id);
});
$answer['success'] = 1;
$answer['message'] = 'Document deleted.';
} catch (PDOException $e) {
$answer['message'] = 'Database error, please try again.';
http_response_code(500);
} catch (Exception $e) {
$answer['message'] = $e->getMessage();
http_response_code(400);
}
exit(json_encode($answer));
+34
View File
@@ -0,0 +1,34 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require_role($user_role, ['owner', 'admin']);
require '../../../assets/utils/classes/OrderManager.php';
require '../../../assets/utils/classes/WarehouseManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid order ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id) {
$mgr = new OrderManager($pdo, $company_id);
$mgr->softDelete($id);
});
$answer['success'] = 1;
$answer['message'] = 'Sales order deleted.';
} catch (PDOException $e) {
$answer['message'] = 'Database error, please try again.';
http_response_code(500);
} catch (Exception $e) {
$answer['message'] = $e->getMessage();
http_response_code(400);
}
exit(json_encode($answer));
+34
View File
@@ -0,0 +1,34 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require_role($user_role, ['owner', 'admin']);
require '../../../assets/utils/classes/ReturnManager.php';
require '../../../assets/utils/classes/WarehouseManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid return ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id) {
$mgr = new ReturnManager($pdo, $company_id);
$mgr->softDelete($id);
});
$answer['success'] = 1;
$answer['message'] = 'Return deleted.';
} catch (PDOException $e) {
$answer['message'] = 'Database error, please try again.';
http_response_code(500);
} catch (Exception $e) {
$answer['message'] = $e->getMessage();
http_response_code(400);
}
exit(json_encode($answer));
+1 -1
View File
@@ -3,7 +3,7 @@
require '../../../assets/utils/db_auth.php';
require_role($user_role, ['owner', 'admin', 'staff']);
require '../../../assets/utils/classes/OrderManager.php';
require '../../../assets/utils/classes/CompanySettingManager.php';
require_once '../../../assets/utils/classes/CompanySettingManager.php';
$id = (int)($data['id'] ?? 0);
$tracking_number = trim((string)($data['shipping_tracking_number'] ?? ''));
+22
View File
@@ -158,6 +158,8 @@
<script>
const can_delete = ['admin', 'owner'].includes(user_role);
function doc_type_badge(doc_type) {
const map = {
'invoice': '<span class="badge bg-primary">Invoice</span>',
@@ -269,6 +271,7 @@
onclick="void_invoice(${inv.id})">
<i class="ti ti-ban fs-5"></i>
</a>` : ''}
${can_delete ? `<a href="javascript:void(0);" class="link-danger ms-2" title="Delete" onclick="delete_invoice(${inv.id})"><i class="ti ti-trash fs-5"></i></a>` : ''}
</td>
</tr>`;
});
@@ -310,6 +313,7 @@
<a href="<?php echo $server_url?>order/manage_invoice.php?id=${cn.id}" title="View">
<i class="ti ti-eye fs-5"></i>
</a>
${can_delete ? `<a href="javascript:void(0);" class="link-danger ms-2" title="Delete" onclick="delete_invoice(${cn.id})"><i class="ti ti-trash fs-5"></i></a>` : ''}
</td>
</tr>`;
});
@@ -342,6 +346,24 @@
// ── Boot ─────────────────────────────────────────────────────────────────
function delete_invoice(id) {
bootbox.confirm({
message: 'Delete this document? Any GL entry will be removed.',
buttons: { confirm: { label: 'Delete', className: 'btn-danger' }, cancel: { label: 'Back', className: 'btn-secondary' } },
callback: function(result) {
if (!result) return;
ajax_request({
url: '<?php echo $server_url?>order/api/engine/delete_invoice.php',
autoPrepare: false,
checkRequired: 0,
action: 'delete',
data: { id: id },
onSuccess: function() { retrieve_invoices(); }
});
}
});
}
$(async function() {
await load_dept_cache();
populate_dept_filter('filter_dept');
+26
View File
@@ -146,6 +146,10 @@
<i class="ti ti-ban me-1"></i>Void Invoice
</button>
<button class="btn btn-outline-danger w-100 d-none" id="btn_delete"
onclick="delete_invoice()">
<i class="ti ti-trash me-1"></i>Delete
</button>
</div>
</div>
@@ -160,6 +164,8 @@
<script>
// custom.js globals: format_number()
const can_delete = ['admin', 'owner'].includes(user_role);
var invoice_id = <?php echo $invoice_id; ?>;
var invoice_data = null;
@@ -223,6 +229,7 @@
!is_invoice ? 'Only original invoices can be voided'
: status === 0 ? 'Invoice has not been issued yet'
: status === 4 ? 'Invoice is already void' : '');
$('#btn_delete').toggleClass('d-none', !can_delete || !invoice_id);
}
@@ -344,6 +351,25 @@
function delete_invoice() {
bootbox.confirm({
message: 'Delete this document? Any GL entry will be removed.',
buttons: { confirm: { label: 'Delete', className: 'btn-danger' }, cancel: { label: 'Back', className: 'btn-secondary' } },
callback: function(result) {
if (!result) return;
ajax_request({
url: '<?php echo $server_url?>order/api/engine/delete_invoice.php',
autoPrepare: false,
checkRequired: 0,
action: 'delete',
data: { id: invoice_id },
onSuccess: function() { window.location.href = '<?php echo $server_url?>order/invoice.php'; }
});
}
});
}
function void_invoice() {
bootbox.confirm({
message: '<strong>Void this invoice?</strong><br>This action cannot be undone.',
+27
View File
@@ -216,6 +216,11 @@
<i class="ti ti-x me-1"></i>Cancel Order
</button>
<button class="btn btn-outline-danger w-100 d-none" id="btn_delete"
onclick="delete_order()">
<i class="ti ti-trash me-1"></i>Delete Order
</button>
</div>
</div>
@@ -230,6 +235,8 @@
<script>
// custom.js globals: remove_item(), init_product_search_inputs(), format_number()
const can_delete = ['admin', 'owner'].includes(user_role);
var order_id = <?php echo $order_id; ?>;
var order_source = <?php echo json_encode($source); ?>;
var order_source_id = <?php echo (int)$source_id; ?>;
@@ -426,6 +433,7 @@
is_pending ? 'Assign warehouses and save first. The order will move to Draft.' : 'Order must be in draft to confirm');
set_btn_state('#btn_cancel', can_cancel && !is_cancelled,
is_cancelled ? 'Order is already cancelled' : 'Only draft or confirmed orders can be cancelled');
$('#btn_delete').toggleClass('d-none', !can_delete || !order_id);
set_btn_state('#btn_tracking', can_update_tracking,
is_cancelled ? 'Order is cancelled'
@@ -631,6 +639,25 @@
// ── Cancel order ──────────────────────────────────────────────────────────
function delete_order() {
bootbox.confirm({
message: 'Delete this order? Unconfirmed stock-out rows will also be removed.',
buttons: { confirm: { label: 'Delete', className: 'btn-danger' }, cancel: { label: 'Back', className: 'btn-secondary' } },
callback: function(result) {
if (!result) return;
ajax_request({
url: '<?php echo $server_url?>order/api/engine/delete_order.php',
autoPrepare: false,
checkRequired: 0,
action: 'delete',
data: { id: order_id },
onSuccess: function() { window.location.href = '<?php echo $server_url?>order/order.php'; }
});
}
});
}
function cancel_order() {
bootbox.confirm({
message: 'Cancel this order? All pending stock-out rows will be cancelled.',
+27
View File
@@ -189,6 +189,11 @@
<i class="ti ti-x me-1"></i>Cancel Return
</button>
<button class="btn btn-outline-danger w-100 d-none" id="btn_delete"
onclick="delete_return()">
<i class="ti ti-trash me-1"></i>Delete Return
</button>
</div>
</div>
@@ -202,6 +207,8 @@
<script>
// custom.js globals: escape_html(), format_number()
const can_delete = ['admin', 'owner'].includes(user_role);
var return_id = <?php echo $return_id; ?>;
var return_data = null;
var order_data = null;
@@ -766,6 +773,7 @@
set_btn_state('#btn_cancel', is_draft || status === 1,
status === -1 ? 'Return is already cancelled' : 'Return is completed and cannot be cancelled');
$('#btn_delete').toggleClass('d-none', !can_delete || !return_id);
}
@@ -865,6 +873,25 @@
// ── Cancel return ─────────────────────────────────────────────────────────
function delete_return() {
bootbox.confirm({
message: 'Delete this return? Unconfirmed stock rows will be removed.',
buttons: { confirm: { label: 'Delete', className: 'btn-danger' }, cancel: { label: 'Back', className: 'btn-secondary' } },
callback: function(result) {
if (!result) return;
ajax_request({
url: '<?php echo $server_url?>order/api/engine/delete_return.php',
autoPrepare: false,
checkRequired: 0,
action: 'delete',
data: { id: return_id },
onSuccess: function() { window.location.href = '<?php echo $server_url?>order/return.php'; }
});
}
});
}
function cancel_return() {
bootbox.confirm({
message: 'Cancel this return request?',
+22
View File
@@ -162,6 +162,8 @@
<script>
const can_delete = ['admin', 'owner'].includes(user_role);
// ── Status helpers ────────────────────────────────────────────────────────
function order_status_badge(status) {
status = parseInt(status);
@@ -268,6 +270,7 @@
onclick="cancel_order(${o.id})">
<i class="ti ti-x fs-5"></i>
</a>` : ''}
${can_delete ? `<a href="javascript:void(0);" class="link-danger ms-2" title="Delete" onclick="delete_order(${o.id})"><i class="ti ti-trash fs-5"></i></a>` : ''}
</td>
</tr>`;
});
@@ -301,6 +304,25 @@
}
function delete_order(id) {
bootbox.confirm({
message: 'Delete this order? Unconfirmed stock-out rows will also be removed.',
buttons: { confirm: { label: 'Delete', className: 'btn-danger' }, cancel: { label: 'Back', className: 'btn-secondary' } },
callback: function(result) {
if (!result) return;
ajax_request({
url: '<?php echo $server_url?>order/api/engine/delete_order.php',
autoPrepare: false,
checkRequired: 0,
action: 'delete',
data: { id: id },
onSuccess: function() { retrieve_orders(); }
});
}
});
}
// ── Boot ─────────────────────────────────────────────────────────────────
$(async function() {
await load_dept_cache();
+21
View File
@@ -99,6 +99,8 @@
<script>
const can_delete = ['admin', 'owner'].includes(user_role);
function return_status_badge(status) {
const map = {
'-1': '<span class="badge bg-secondary">Cancelled</span>',
@@ -182,6 +184,7 @@
onclick="cancel_return(${r.id})">
<i class="ti ti-x fs-5"></i>
</a>` : ''}
${can_delete ? `<a href="javascript:void(0);" class="link-danger ms-2" title="Delete" onclick="delete_return(${r.id})"><i class="ti ti-trash fs-5"></i></a>` : ''}
</td>
</tr>`;
});
@@ -212,6 +215,24 @@
}
function delete_return(id) {
bootbox.confirm({
message: 'Delete this return? Unconfirmed stock rows will be removed.',
buttons: { confirm: { label: 'Delete', className: 'btn-danger' }, cancel: { label: 'Back', className: 'btn-secondary' } },
callback: function(result) {
if (!result) return;
ajax_request({
url: '<?php echo $server_url?>order/api/engine/delete_return.php',
autoPrepare: false,
checkRequired: 0,
action: 'delete',
data: { id: id },
onSuccess: function() { retrieve_returns(); }
});
}
});
}
$(async function() {
await load_dept_cache();
populate_dept_filter('filter_dept');