Orders: order, return, invoice

This commit is contained in:
Thanakorn S
2026-05-02 16:49:21 +07:00
parent ac4fd9a5ad
commit a90975d9f1
47 changed files with 4838 additions and 99 deletions
+33
View File
@@ -0,0 +1,33 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/OrderManager.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, $logging) {
$order = new OrderManager($pdo, $company_id);
$order->cancelOrder($id, $logging);
});
$answer['success'] = 1;
$answer['message'] = 'Order cancelled.';
} 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));
?>
+44
View File
@@ -0,0 +1,44 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id) {
$sth = $pdo->prepare(
"SELECT status FROM td_return
WHERE company_id = :company_id AND id = :id"
);
$sth->execute([':company_id' => $company_id, ':id' => $id]);
$row = $sth->fetch(PDO::FETCH_ASSOC);
if (!$row) throw new Exception("Return not found.");
if ((int)$row['status'] !== 0) throw new Exception("Only draft returns can be cancelled.");
$pdo->prepare(
"UPDATE td_return SET status = -1
WHERE id = :id AND company_id = :company_id"
)->execute([':id' => $id, ':company_id' => $company_id]);
});
$answer['success'] = 1;
$answer['message'] = 'Return cancelled.';
} 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));
?>
+48
View File
@@ -0,0 +1,48 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/OrderManager.php';
require '../../../assets/utils/classes/StockManager.php';
require '../../../assets/utils/classes/WarehouseManager.php';
require '../../../assets/utils/classes/InvoiceManager.php';
require '../../../assets/utils/classes/CompanySettingManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid order ID.';
http_response_code(400);
exit(json_encode($answer));
}
$csm = new CompanySettingManager($pdo1, $company_id);
$auto_approve = (int)$csm->get('default_stock_status') === 1;
$auto_invoice = (int)$csm->get('auto_invoice_and_credit_note') === 1;
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id, $logging, $uuid, $auto_approve, $auto_invoice) {
$order = new OrderManager($pdo, $company_id);
$order->confirmOrder($id, $uuid, $logging, $auto_approve);
if ($auto_invoice) {
$invMgmt = new InvoiceManager($pdo, $company_id);
$invMgmt->createFromOrder($id, $logging);
}
});
$answer['success'] = 1;
$answer['message'] = 'Order confirmed.';
$answer['auto_approved'] = $auto_approve;
$answer['auto_invoice'] = $auto_invoice;
} 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));
?>
+42
View File
@@ -0,0 +1,42 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/ReturnManager.php';
require '../../../assets/utils/classes/InvoiceManager.php';
require '../../../assets/utils/classes/WarehouseManager.php';
require '../../../assets/utils/classes/CompanySettingManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid ID.';
http_response_code(400);
exit(json_encode($answer));
}
$csm = new CompanySettingManager($pdo1, $company_id);
$auto_cn = (int)$csm->get('auto_invoice_and_credit_note') === 1;
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id, $logging, $uuid, $auto_cn) {
$ret = new ReturnManager($pdo, $company_id);
$whMgmt = new WarehouseManager($pdo, $company_id);
$invMgmt = new InvoiceManager($pdo, $company_id);
$ret->confirmReturn($id, $uuid, $logging, $whMgmt, $invMgmt, $auto_cn);
});
$answer['success'] = 1;
$answer['message'] = 'Return confirmed.';
$answer['auto_cn'] = $auto_cn;
} 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));
?>
+33
View File
@@ -0,0 +1,33 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/InvoiceManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id, $logging) {
$inv = new InvoiceManager($pdo, $company_id);
$inv->issueInvoice($id, $logging);
});
$answer['success'] = 1;
$answer['message'] = 'Invoice issued.';
} 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));
?>
+24
View File
@@ -0,0 +1,24 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/InvoiceManager.php';
try {
dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging) {
$inv = new InvoiceManager($pdo, $company_id);
$inv->saveInvoice($data, $logging);
});
$answer['success'] = 1;
} 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));
?>
+32
View File
@@ -0,0 +1,32 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/OrderManager.php';
// Decode items JSON string back to array
$data['items'] = json_decode($data['items'] ?? '[]', true) ?: [];
try {
$new_id = null;
dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging, &$new_id) {
$order = new OrderManager($pdo, $company_id);
$new_id = $order->saveOrder($data, $logging);
});
$answer['success'] = 1;
if ($new_id) {
$answer['new_id'] = $new_id;
}
} 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));
?>
+31
View File
@@ -0,0 +1,31 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/ReturnManager.php';
$data['items'] = json_decode($data['items'] ?? '[]', true) ?: [];
try {
$new_id = null;
dbTransaction($pdo2, function($pdo) use ($data, $company_id, $logging, &$new_id) {
$ret = new ReturnManager($pdo, $company_id);
$new_id = $ret->saveReturn($data, $logging);
});
$answer['success'] = 1;
if ($new_id) {
$answer['new_id'] = $new_id;
}
} 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));
?>
@@ -0,0 +1,35 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/InvoiceManager.php';
$order_id = (int)($data['order_id'] ?? 0);
if (!$order_id) {
$answer['message'] = 'Invalid order ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
$new_id = null;
dbTransaction($pdo2, function($pdo) use ($order_id, $company_id, $logging, &$new_id) {
$inv = new InvoiceManager($pdo, $company_id);
$new_id = $inv->createFromOrder($order_id, $logging);
});
$answer['success'] = 1;
$answer['new_id'] = $new_id;
} 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));
?>
+57
View File
@@ -0,0 +1,57 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/InvoiceManager.php';
$inv = new InvoiceManager($pdo2, $company_id);
$id = (int)($data['invoice_id'] ?? 0);
if ($id > 0) {
$result = $inv->getInvoiceById($id);
if (!$result) {
http_response_code(404);
$answer['message'] = 'Invoice not found.';
exit(json_encode($answer));
}
$answer['output'] = $result;
// Fetch linked credit notes for invoice detail page
if ($result['doc_type'] === 'invoice') {
$sth = $pdo2->prepare(
"SELECT * FROM td_invoice
WHERE company_id = :company_id
AND ref_invoice_id = :ref_id
AND doc_type = 'credit_note'
ORDER BY id DESC"
);
$sth->execute([':company_id' => $company_id, ':ref_id' => $id]);
$answer['credit_notes'] = $sth->fetchAll(PDO::FETCH_ASSOC);
}
} else {
$order_id = (int)($data['order_id'] ?? 0);
$doc_type = $data['doc_type'] ?? '';
$answer['output'] = $inv->getInvoiceList($order_id, $doc_type);
// For credit note tab — join ref invoice number
if ($doc_type === 'credit_note') {
$rows = $answer['output'];
foreach ($rows as &$row) {
if ((int)$row['ref_invoice_id'] > 0) {
$sth = $pdo2->prepare(
"SELECT invoice_number FROM td_invoice
WHERE id = :id AND company_id = :company_id"
);
$sth->execute([':id' => $row['ref_invoice_id'], ':company_id' => $company_id]);
$row['ref_invoice_number'] = $sth->fetchColumn() ?: '—';
} else {
$row['ref_invoice_number'] = '—';
}
}
unset($row);
$answer['output'] = $rows;
}
}
$answer['success'] = 1;
exit(json_encode($answer));
?>
+73
View File
@@ -0,0 +1,73 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/OrderManager.php';
$order = new OrderManager($pdo2, $company_id);
$id = (int)($data['id'] ?? 0);
if ($id > 0) {
$result = $order->getOrderById($id);
if (!$result) {
http_response_code(404);
$answer['message'] = 'Order not found.';
exit(json_encode($answer));
}
$answer['output'] = $result;
// Check if an active invoice already exists for this order
$sth = $pdo2->prepare(
"SELECT COUNT(*) FROM td_invoice
WHERE company_id = :company_id
AND order_id = :order_id
AND doc_type = 'invoice'
AND status != 4"
);
$sth->execute([':company_id' => $company_id, ':order_id' => $id]);
$answer['has_active_invoice'] = (int)$sth->fetchColumn() > 0;
// Calculate returnable items — order items minus already confirmed returns
$order_items = $result['items'] ?? '[]';
// Aggregate returned qty per item_id from confirmed returns
$sth = $pdo2->prepare(
"SELECT items FROM td_return
WHERE company_id = :company_id
AND order_id = :order_id
AND status = 1"
);
$sth->execute([':company_id' => $company_id, ':order_id' => $id]);
$returned_qty = [];
foreach ($sth->fetchAll(PDO::FETCH_COLUMN) as $items_json) {
$items = json_decode($items_json, true) ?: [];
foreach ($items as $item) {
$item_id = (int)($item['item_id'] ?? -1);
if ($item_id < 0) continue;
$returned_qty[$item_id] = ($returned_qty[$item_id] ?? 0) + (float)$item['quantity'];
}
}
$returnable = [];
foreach ($order_items as $i => $item) {
$already_returned = $returned_qty[$i] ?? 0;
$remaining = (float)$item['quantity'] - $already_returned;
if ($remaining > 0) {
$returnable[] = array_merge($item, [
'item_id' => $i,
'original_qty' => (float)$item['quantity'],
'returned_qty' => $already_returned,
'returnable_qty' => $remaining,
]);
}
}
$answer['returnable_items'] = $returnable;
} else {
$answer['output'] = $order->getOrderList();
}
$answer['success'] = 1;
exit(json_encode($answer));
?>
+24
View File
@@ -0,0 +1,24 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/ReturnManager.php';
$ret = new ReturnManager($pdo2, $company_id);
$id = (int)($data['id'] ?? 0);
if ($id > 0) {
$result = $ret->getReturnById($id);
if (!$result) {
http_response_code(404);
$answer['message'] = 'Return not found.';
exit(json_encode($answer));
}
$answer['output'] = $result;
} else {
$order_id = (int)($data['order_id'] ?? 0);
$answer['output'] = $ret->getReturnList($order_id);
}
$answer['success'] = 1;
exit(json_encode($answer));
?>
@@ -0,0 +1,71 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/InvoiceManager.php';
$id = (int)($data['invoice_id'] ?? 0);
$status = (int)($data['status'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid invoice ID.';
http_response_code(400);
exit(json_encode($answer));
}
// 2=paid 3=overdue 4=void — issue (0→1) handled by issue_invoice.php
if (!in_array($status, [2, 3, 4], true)) {
$answer['message'] = 'Invalid status value.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $status, $company_id, $logging) {
$sth = $pdo->prepare(
"SELECT status, doc_type, `log` FROM td_invoice
WHERE company_id = :company_id AND id = :id"
);
$sth->execute([':company_id' => $company_id, ':id' => $id]);
$row = $sth->fetch(PDO::FETCH_ASSOC);
if (!$row) throw new Exception("Invoice not found.");
if ($row['doc_type'] !== 'invoice') throw new Exception("Only invoices can be updated this way.");
$current = (int)$row['status'];
if ($current === 4) throw new Exception("Invoice is already void.");
if ($current === 0) throw new Exception("Issue the invoice before changing payment status.");
$log = json_decode($row['log'] ?? '[]', true) ?: [];
$log[] = array_merge($logging, [
'action' => 'update_status',
'status' => $status,
'previous_status' => $current,
]);
$pdo->prepare(
"UPDATE td_invoice SET status = :status, `log` = :log
WHERE id = :id AND company_id = :company_id"
)->execute([
':status' => $status,
':log' => json_encode($log),
':id' => $id,
':company_id' => $company_id,
]);
});
$answer['success'] = 1;
$answer['message'] = 'Invoice status updated.';
} 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));
?>
@@ -0,0 +1,69 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
$id = (int)($data['id'] ?? 0);
$payment_status = (int)($data['payment_status'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid order ID.';
http_response_code(400);
exit(json_encode($answer));
}
// Validate payment_status range
// 0=unpaid 1=paid 2=partial 3=refunded
if (!in_array($payment_status, [0, 1, 2, 3], true)) {
$answer['message'] = 'Invalid payment status.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $payment_status, $company_id, $logging) {
// Load order — verify ownership and that order is not cancelled
$sth = $pdo->prepare(
"SELECT status, payment_status, `log` FROM td_order
WHERE company_id = :company_id AND id = :id"
);
$sth->execute([':company_id' => $company_id, ':id' => $id]);
$row = $sth->fetch(PDO::FETCH_ASSOC);
if (!$row) throw new Exception("Order not found.");
if ((int)$row['status'] === -1) throw new Exception("Cannot update payment status of a cancelled order.");
$log = json_decode($row['log'] ?? '[]', true) ?: [];
$log[] = array_merge($logging, [
'action' => 'update_payment_status',
'payment_status' => $payment_status,
'previous_status' => (int)$row['payment_status'],
]);
$pdo->prepare(
"UPDATE td_order SET
payment_status = :payment_status,
`log` = :log
WHERE id = :id AND company_id = :company_id"
)->execute([
':payment_status' => $payment_status,
':log' => json_encode($log),
':id' => $id,
':company_id' => $company_id,
]);
});
$answer['success'] = 1;
$answer['message'] = 'Payment status updated.';
} 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));
?>
+33
View File
@@ -0,0 +1,33 @@
<?php
session_start();
require '../../../assets/utils/db_auth.php';
require '../../../assets/utils/classes/InvoiceManager.php';
$id = (int)($data['id'] ?? 0);
if (!$id) {
$answer['message'] = 'Invalid ID.';
http_response_code(400);
exit(json_encode($answer));
}
try {
dbTransaction($pdo2, function($pdo) use ($id, $company_id, $logging) {
$inv = new InvoiceManager($pdo, $company_id);
$inv->voidInvoice($id, $logging);
});
$answer['success'] = 1;
$answer['message'] = 'Invoice voided.';
} 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));
?>