accounting modules
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
require '../../../assets/utils/classes/OrderManager.php';
|
||||
|
||||
$quotation_id = (int)($data['quotation_id'] ?? 0);
|
||||
if (!$quotation_id) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Quotation ID required']));
|
||||
}
|
||||
|
||||
// Load quotation — must be Accepted (status=2)
|
||||
$sth = $pdo2->prepare(
|
||||
"SELECT q.*, c.contact_name
|
||||
FROM ac_quotation q
|
||||
LEFT JOIN md_contact c ON c.id = q.contact_id
|
||||
WHERE q.id = :id AND q.company_id = :cid AND q.status = 2
|
||||
LIMIT 1"
|
||||
);
|
||||
$sth->execute([':id' => $quotation_id, ':cid' => $company_id]);
|
||||
$q = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$q) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Quotation not found or not in Accepted status']));
|
||||
}
|
||||
|
||||
// Check not already converted
|
||||
if ((int)$q['order_id'] > 0) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Quotation has already been converted to an order']));
|
||||
}
|
||||
|
||||
// Map quotation items → order items (warehouse_id left 0 = to be filled by WMS)
|
||||
$qt_items = json_decode($q['items'] ?? '[]', true) ?: [];
|
||||
$ord_items = [];
|
||||
foreach ($qt_items as $i => $qi) {
|
||||
$ord_items[] = [
|
||||
'item_id' => $i,
|
||||
'product_sku' => $qi['product_sku'] ?? '',
|
||||
'product_name' => $qi['description'] ?? '',
|
||||
'warehouse_id' => 0,
|
||||
'zone' => '',
|
||||
'aisle' => '',
|
||||
'rack' => '',
|
||||
'lot_number' => '',
|
||||
'serial_number' => '',
|
||||
'quantity' => (float)($qi['quantity'] ?? 0),
|
||||
'price' => (float)($qi['unit_price'] ?? 0),
|
||||
'total_price' => (float)($qi['total_price'] ?? 0),
|
||||
'stock_out_id' => 0,
|
||||
];
|
||||
}
|
||||
|
||||
$order_data = [
|
||||
'quotation_id' => $quotation_id,
|
||||
'contact_id' => (int)$q['contact_id'],
|
||||
'order_date' => date('Y-m-d'),
|
||||
'items' => $ord_items,
|
||||
'discount' => (float)$q['discount'],
|
||||
'tax' => (float)$q['tax'],
|
||||
'shipping_fee' => 0,
|
||||
'shipping_tracking_number' => '',
|
||||
'notes' => $q['notes'] ?? '',
|
||||
];
|
||||
|
||||
try {
|
||||
$new_order_id = null;
|
||||
|
||||
dbTransaction($pdo2, function($pdo) use ($order_data, $company_id, $logging, $quotation_id, &$new_order_id) {
|
||||
$order = new OrderManager($pdo, $company_id);
|
||||
$new_order_id = $order->saveOrder($order_data, $logging);
|
||||
|
||||
// Mark quotation as Converted and link order_id
|
||||
$pdo->prepare(
|
||||
"UPDATE ac_quotation SET status = 5, order_id = :oid
|
||||
WHERE id = :id AND company_id = :cid"
|
||||
)->execute([':oid' => $new_order_id, ':id' => $quotation_id, ':cid' => $company_id]);
|
||||
});
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Order created successfully';
|
||||
$answer['order_id'] = $new_order_id;
|
||||
|
||||
} catch (Exception $e) {
|
||||
$answer['message'] = $e->getMessage();
|
||||
http_response_code(400);
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$action = $data['action'] ?? '';
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
// ── Generate quotation number ─────────────────────────────────────────────────
|
||||
function generate_quotation_number(PDO $pdo, int $cid): string {
|
||||
$prefix = 'QT-' . date('Ym') . '-';
|
||||
$sql = "SELECT quotation_number FROM ac_quotation
|
||||
WHERE company_id = :cid AND quotation_number LIKE :prefix
|
||||
ORDER BY id DESC LIMIT 1";
|
||||
$sth = $pdo->prepare($sql);
|
||||
$sth->execute([':cid' => $cid, ':prefix' => $prefix . '%']);
|
||||
$last = $sth->fetchColumn();
|
||||
$seq = $last ? ((int)substr($last, -4) + 1) : 1;
|
||||
return $prefix . str_pad($seq, 4, '0', STR_PAD_LEFT);
|
||||
}
|
||||
|
||||
// ── Compute totals from items ─────────────────────────────────────────────────
|
||||
function compute_totals(array $items, float $discount, float $tax): array {
|
||||
$subtotal = array_sum(array_map(fn($i) => (float)($i['total_price'] ?? 0), $items));
|
||||
$grand = $subtotal - $discount + $tax;
|
||||
return [round($subtotal, 4), round(max(0, $grand), 4)];
|
||||
}
|
||||
|
||||
if ($action === 'create') {
|
||||
|
||||
$items = json_decode($data['items'] ?? '[]', true) ?: [];
|
||||
$discount = (float)($data['discount'] ?? 0);
|
||||
$tax = (float)($data['tax'] ?? 0);
|
||||
[$subtotal, $grand] = compute_totals($items, $discount, $tax);
|
||||
|
||||
$qnum = generate_quotation_number($pdo2, $company_id);
|
||||
|
||||
$sql = "INSERT INTO ac_quotation
|
||||
(company_id, uuid, quotation_number, contact_id, quotation_date, valid_until,
|
||||
status, subtotal, discount, tax, grand_total, items, notes, log, created_at)
|
||||
VALUES
|
||||
(:cid, :uuid, :qnum, :contact_id, :qdate, :valid_until,
|
||||
0, :sub, :disc, :tax, :grand, :items, :notes, :log, NOW())";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([
|
||||
':cid' => $company_id,
|
||||
':uuid' => $uuid,
|
||||
':qnum' => $qnum,
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':qdate' => $data['quotation_date'] ?: null,
|
||||
':valid_until'=> $data['valid_until'] ?: null,
|
||||
':sub' => $subtotal,
|
||||
':disc' => $discount,
|
||||
':tax' => $tax,
|
||||
':grand' => $grand,
|
||||
':items' => json_encode($items),
|
||||
':notes' => $data['notes'] ?? '',
|
||||
':log' => json_encode($logging),
|
||||
]);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Quotation created';
|
||||
$answer['new_id'] = (int)$pdo2->lastInsertId();
|
||||
$answer['quotation_number'] = $qnum;
|
||||
|
||||
} elseif ($action === 'update') {
|
||||
|
||||
if (!$id) { exit(json_encode(['success' => 0, 'message' => 'ID required'])); }
|
||||
|
||||
$items = json_decode($data['items'] ?? '[]', true) ?: [];
|
||||
$discount = (float)($data['discount'] ?? 0);
|
||||
$tax = (float)($data['tax'] ?? 0);
|
||||
[$subtotal, $grand] = compute_totals($items, $discount, $tax);
|
||||
|
||||
$sql = "UPDATE ac_quotation SET
|
||||
contact_id = :contact_id,
|
||||
quotation_date = :qdate,
|
||||
valid_until = :valid_until,
|
||||
subtotal = :sub,
|
||||
discount = :disc,
|
||||
tax = :tax,
|
||||
grand_total = :grand,
|
||||
items = :items,
|
||||
notes = :notes
|
||||
WHERE id = :id AND company_id = :cid AND status = 0";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([
|
||||
':contact_id' => (int)($data['contact_id'] ?? 0),
|
||||
':qdate' => $data['quotation_date'] ?: null,
|
||||
':valid_until' => $data['valid_until'] ?: null,
|
||||
':sub' => $subtotal,
|
||||
':disc' => $discount,
|
||||
':tax' => $tax,
|
||||
':grand' => $grand,
|
||||
':items' => json_encode($items),
|
||||
':notes' => $data['notes'] ?? '',
|
||||
':id' => $id,
|
||||
':cid' => $company_id,
|
||||
]);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Quotation updated';
|
||||
|
||||
} else {
|
||||
$answer['message'] = 'Unknown action';
|
||||
}
|
||||
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$sql = "SELECT
|
||||
COUNT(*) AS total,
|
||||
SUM(status = 0) AS draft,
|
||||
SUM(status = 1) AS sent,
|
||||
SUM(status = 2) AS accepted,
|
||||
SUM(status = 3) AS rejected
|
||||
FROM ac_quotation
|
||||
WHERE company_id = :cid";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([':cid' => $company_id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['output'] = $row;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
|
||||
if ($id) {
|
||||
// Single quotation
|
||||
$sql = "SELECT q.*, c.contact_name
|
||||
FROM ac_quotation q
|
||||
LEFT JOIN md_contact c ON c.id = q.contact_id
|
||||
WHERE q.id = :id AND q.company_id = :cid
|
||||
LIMIT 1";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([':id' => $id, ':cid' => $company_id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) { exit(json_encode(['success' => 0, 'message' => 'Not found'])); }
|
||||
$row['items'] = json_decode($row['items'] ?? '[]', true);
|
||||
$answer['output'] = $row;
|
||||
} else {
|
||||
// List
|
||||
$sql = "SELECT q.*, c.contact_name
|
||||
FROM ac_quotation q
|
||||
LEFT JOIN md_contact c ON c.id = q.contact_id
|
||||
WHERE q.company_id = :cid
|
||||
ORDER BY q.id DESC";
|
||||
$sth = $pdo2->prepare($sql);
|
||||
$sth->execute([':cid' => $company_id]);
|
||||
$answer['output'] = $sth->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$answer['success'] = 1;
|
||||
exit(json_encode($answer));
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
session_start();
|
||||
require '../../../assets/utils/db_auth.php';
|
||||
|
||||
$id = (int)($data['id'] ?? 0);
|
||||
$action = $data['action_type'] ?? '';
|
||||
|
||||
if (!$id || !$action) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'ID and action required']));
|
||||
}
|
||||
|
||||
// Load current status
|
||||
$sth = $pdo2->prepare("SELECT status FROM ac_quotation WHERE id = :id AND company_id = :cid LIMIT 1");
|
||||
$sth->execute([':id' => $id, ':cid' => $company_id]);
|
||||
$row = $sth->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) { exit(json_encode(['success' => 0, 'message' => 'Not found'])); }
|
||||
|
||||
$cur = (int)$row['status'];
|
||||
|
||||
$transitions = [
|
||||
'send' => ['from' => [0], 'to' => 1, 'label' => 'Sent'],
|
||||
'accept' => ['from' => [1], 'to' => 2, 'label' => 'Accepted'],
|
||||
'reject' => ['from' => [1], 'to' => 3, 'label' => 'Rejected'],
|
||||
'reopen' => ['from' => [1,2,3],'to' => 0, 'label' => 'Draft'],
|
||||
'cancel' => ['from' => [0,1], 'to' => -1,'label' => 'Cancelled'],
|
||||
];
|
||||
|
||||
if (!isset($transitions[$action])) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Invalid action']));
|
||||
}
|
||||
|
||||
$t = $transitions[$action];
|
||||
if (!in_array($cur, $t['from'], true)) {
|
||||
exit(json_encode(['success' => 0, 'message' => 'Transition not allowed from current status']));
|
||||
}
|
||||
|
||||
$sth = $pdo2->prepare("UPDATE ac_quotation SET status = :status WHERE id = :id AND company_id = :cid");
|
||||
$sth->execute([':status' => $t['to'], ':id' => $id, ':cid' => $company_id]);
|
||||
|
||||
$answer['success'] = 1;
|
||||
$answer['message'] = 'Status updated to ' . $t['label'];
|
||||
exit(json_encode($answer));
|
||||
Reference in New Issue
Block a user