commit
This commit is contained in:
222
src/modules/quotations/service.ts
Normal file
222
src/modules/quotations/service.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user