Files
nextjs-elysia-allaos/src/modules/quotations/service.ts
phaichayon a330abf9b6 commit
2026-04-23 15:37:01 +07:00

223 lines
5.8 KiB
TypeScript

import type { Quotation, CreateQuotation, UpdateQuotation } from "./model";
// Mock quotations data
const mockQuotations: Quotation[] = [
{
id: "quot-001",
quotationNumber: "QT-2024-001",
branch: "branch-01",
customerId: "cust-001",
customerName: "สมชาย ใจดี",
date: "2024-01-20T00:00:00Z",
validUntil: "2024-02-20T00:00:00Z",
subtotal: 50000,
taxRate: 0.07,
taxAmount: 3500,
totalAmount: 53500,
status: "sent",
notes: "Quotation for office supplies",
createdAt: "2024-01-20T09:00:00Z",
updatedAt: "2024-01-20T09:00:00Z",
},
{
id: "quot-002",
quotationNumber: "QT-2024-002",
branch: "branch-01",
customerId: "cust-002",
customerName: "วิภา สุขสันต์",
date: "2024-02-25T00:00:00Z",
validUntil: "2024-03-25T00:00:00Z",
subtotal: 120000,
taxRate: 0.07,
taxAmount: 8400,
totalAmount: 128400,
status: "accepted",
notes: "Quotation for computer equipment",
createdAt: "2024-02-25T10:30:00Z",
updatedAt: "2024-02-28T14:20:00Z",
},
{
id: "quot-003",
quotationNumber: "QT-2024-003",
branch: "branch-02",
customerId: "cust-004",
customerName: "มานี มีสุข",
date: "2024-03-10T00:00:00Z",
validUntil: "2024-04-10T00:00:00Z",
subtotal: 75000,
taxRate: 0.07,
taxAmount: 5250,
totalAmount: 80250,
status: "draft",
notes: null,
createdAt: "2024-03-10T11:00:00Z",
updatedAt: "2024-03-10T11:00:00Z",
},
{
id: "quot-004",
quotationNumber: "QT-2024-004",
branch: "head-office",
customerId: "cust-007",
customerName: "ภูมิ รักษ์โลก",
date: "2024-04-01T00:00:00Z",
validUntil: "2024-05-01T00:00:00Z",
subtotal: 200000,
taxRate: 0.07,
taxAmount: 14000,
totalAmount: 214000,
status: "sent",
notes: "Quotation for laboratory equipment",
createdAt: "2024-04-01T09:30:00Z",
updatedAt: "2024-04-01T09:30:00Z",
},
];
/**
* Get all quotations for a specific branch
* @param branch - Branch identifier
* @param status - Optional status filter
* @returns Array of quotations
*/
export function getAllQuotations(
branch: string,
status?: "draft" | "sent" | "accepted" | "rejected" | "expired",
): Quotation[] {
let quotations = mockQuotations.filter((q) => q.branch === branch);
if (status) {
quotations = quotations.filter((q) => q.status === status);
}
return quotations;
}
/**
* Get a single quotation by ID and branch
* @param branch - Branch identifier
* @param id - Quotation ID
* @returns Quotation or undefined if not found
*/
export function getQuotationByIdAndBranch(
branch: string,
id: string,
): Quotation | undefined {
const quotation = mockQuotations.find((q) => q.id === id);
// Only return if quotation belongs to the specified branch
if (quotation && quotation.branch === branch) {
return quotation;
}
return undefined;
}
/**
* Calculate tax and total amounts
* @param subtotal - Subtotal amount
* @param taxRate - Tax rate (e.g., 0.07 for 7%)
* @returns Object with taxAmount and totalAmount
*/
function calculateTotals(subtotal: number, taxRate: number) {
const taxAmount = subtotal * taxRate;
const totalAmount = subtotal + taxAmount;
return { taxAmount, totalAmount };
}
/**
* Create a new quotation
* @param data - Quotation creation data
* @returns Newly created quotation
*/
export function createQuotation(data: CreateQuotation): Quotation {
const { taxAmount, totalAmount } = calculateTotals(
data.subtotal,
data.taxRate,
);
const newQuotation: Quotation = {
id: `quot-${Date.now()}`,
quotationNumber: `QT-${new Date().getFullYear()}-${String(mockQuotations.length + 1).padStart(3, "0")}`,
...data,
taxAmount,
totalAmount,
status: data.status || "draft",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
// In a real app, this would save to database
mockQuotations.push(newQuotation);
return newQuotation;
}
/**
* Update an existing quotation
* @param branch - Branch identifier
* @param id - Quotation ID
* @param data - Quotation update data
* @returns Updated quotation or undefined if not found
*/
export function updateQuotation(
branch: string,
id: string,
data: UpdateQuotation,
): Quotation | undefined {
const quotation = getQuotationByIdAndBranch(branch, id);
if (!quotation) {
return undefined;
}
// Recalculate totals if subtotal or taxRate changed
let { taxAmount, totalAmount } = quotation;
if (data.subtotal !== undefined || data.taxRate !== undefined) {
const newSubtotal = data.subtotal ?? quotation.subtotal;
const newTaxRate = data.taxRate ?? quotation.taxRate;
const calculated = calculateTotals(newSubtotal, newTaxRate);
taxAmount = calculated.taxAmount;
totalAmount = calculated.totalAmount;
}
// Merge update data
const updatedQuotation: Quotation = {
...quotation,
...data,
taxAmount,
totalAmount,
updatedAt: new Date().toISOString(),
};
// In a real app, this would update database
const index = mockQuotations.findIndex((q) => q.id === id);
if (index !== -1) {
mockQuotations[index] = updatedQuotation;
}
return updatedQuotation;
}
/**
* Delete a quotation
* @param branch - Branch identifier
* @param id - Quotation ID
* @returns Deleted quotation or undefined if not found
*/
export function deleteQuotation(
branch: string,
id: string,
): Quotation | undefined {
const quotation = getQuotationByIdAndBranch(branch, id);
if (!quotation) {
return undefined;
}
// In a real app, this would delete from database
const index = mockQuotations.findIndex((q) => q.id === id);
if (index !== -1) {
mockQuotations.splice(index, 1);
}
return quotation;
}