223 lines
5.8 KiB
TypeScript
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;
|
|
}
|