159 lines
4.2 KiB
Go
159 lines
4.2 KiB
Go
package logic
|
|
|
|
import (
|
|
"math"
|
|
)
|
|
|
|
// Rate represents standard GST rates
|
|
type Rate float64
|
|
|
|
const (
|
|
Rate0 Rate = 0.0
|
|
Rate5 Rate = 0.05
|
|
Rate12 Rate = 0.12
|
|
Rate18 Rate = 0.18
|
|
Rate28 Rate = 0.28
|
|
)
|
|
|
|
// CustomerType distinguishes between B2B (Wholesale) and B2C (Retail)
|
|
type CustomerType string
|
|
|
|
const (
|
|
CustomerWholesale CustomerType = "wholesale"
|
|
CustomerRetail CustomerType = "retail"
|
|
)
|
|
|
|
// Product represents a catalog item
|
|
type Product struct {
|
|
SKU string
|
|
Name string
|
|
HSNCode string
|
|
BasePrice float64 // Price before tax
|
|
WholesalePrice float64 // Discounted price for B2B
|
|
GSTRate Rate
|
|
SmallOrderQty int // Minimum quantity threshold
|
|
SmallOrderFee float64 // Convenience fee when quantity is below threshold
|
|
Unit string // Unit of measurement (e.g., "pcs", "kg", "box")
|
|
}
|
|
|
|
// LineItem represents a single row in the invoice
|
|
type LineItem struct {
|
|
Product Product
|
|
Quantity int
|
|
UnitPrice float64 // Actual price applied (wholesale vs retail)
|
|
TaxableVal float64 // Quantity * UnitPrice
|
|
CGSTAmount float64
|
|
SGSTAmount float64
|
|
IGSTAmount float64
|
|
TotalAmount float64
|
|
}
|
|
|
|
// Invoice represents the full bill
|
|
type Invoice struct {
|
|
LineItems []LineItem
|
|
SubTotal float64
|
|
TotalCGST float64
|
|
TotalSGST float64
|
|
TotalIGST float64
|
|
ConvenienceFee float64 // Flat fee for small orders (before tax)
|
|
ConvenienceFeeTax float64 // GST on convenience fee (18% fixed)
|
|
GrandTotal float64
|
|
CustomerType CustomerType
|
|
IsInterState bool // True if selling to a different state (IGST applies)
|
|
CompanyDetails string // Multiline company details (displayed above invoice table)
|
|
BuyerDetails string // Multiline buyer details (displayed above bank details)
|
|
BuyerName string // Buyer's name
|
|
BankDetails string // Multiline bank details (displayed at bottom of invoice)
|
|
}
|
|
|
|
// Calculator handles the GST logic
|
|
type Calculator struct{}
|
|
|
|
// NewCalculator creates a new calculator instance
|
|
func NewCalculator() *Calculator {
|
|
return &Calculator{}
|
|
}
|
|
|
|
// CalculateLineItem computes taxes for a single line
|
|
func (c *Calculator) CalculateLineItem(p Product, qty int, custType CustomerType, isInterState bool) LineItem {
|
|
// Determine price based on customer type
|
|
price := p.BasePrice
|
|
if custType == CustomerWholesale {
|
|
price = p.WholesalePrice
|
|
}
|
|
|
|
taxableVal := price * float64(qty)
|
|
rate := float64(p.GSTRate)
|
|
|
|
var cgst, sgst, igst float64
|
|
|
|
if isInterState {
|
|
igst = taxableVal * rate
|
|
} else {
|
|
// Intra-state: Split tax between Center and State
|
|
halfRate := rate / 2
|
|
cgst = taxableVal * halfRate
|
|
sgst = taxableVal * halfRate
|
|
}
|
|
|
|
total := taxableVal + cgst + sgst + igst
|
|
|
|
return LineItem{
|
|
Product: p,
|
|
Quantity: qty,
|
|
UnitPrice: price,
|
|
TaxableVal: round(taxableVal),
|
|
CGSTAmount: round(cgst),
|
|
SGSTAmount: round(sgst),
|
|
IGSTAmount: round(igst),
|
|
TotalAmount: round(total),
|
|
}
|
|
}
|
|
|
|
// CalculateInvoice computes totals for the entire invoice
|
|
func (c *Calculator) CalculateInvoice(items []LineItem, fee float64, isInterState bool) Invoice {
|
|
inv := Invoice{
|
|
LineItems: items,
|
|
ConvenienceFee: fee,
|
|
IsInterState: isInterState,
|
|
}
|
|
|
|
for _, item := range items {
|
|
inv.SubTotal += item.TaxableVal
|
|
inv.TotalCGST += item.CGSTAmount
|
|
inv.TotalSGST += item.SGSTAmount
|
|
inv.TotalIGST += item.IGSTAmount
|
|
}
|
|
|
|
// Convenience fee is taxable at 18% fixed rate
|
|
if fee > 0 {
|
|
feeTax := fee * 0.18 // 18% GST on convenience fee
|
|
inv.ConvenienceFeeTax = round(feeTax)
|
|
// Add convenience fee to taxable subtotal
|
|
inv.SubTotal += fee
|
|
// Add convenience fee tax to appropriate tax fields
|
|
if isInterState {
|
|
inv.TotalIGST += inv.ConvenienceFeeTax
|
|
} else {
|
|
// Split between CGST and SGST (9% each)
|
|
inv.TotalCGST += round(feeTax / 2)
|
|
inv.TotalSGST += round(feeTax / 2)
|
|
}
|
|
}
|
|
|
|
inv.GrandTotal = inv.SubTotal + inv.TotalCGST + inv.TotalSGST + inv.TotalIGST
|
|
|
|
// Rounding final totals
|
|
inv.SubTotal = round(inv.SubTotal)
|
|
inv.TotalCGST = round(inv.TotalCGST)
|
|
inv.TotalSGST = round(inv.TotalSGST)
|
|
inv.TotalIGST = round(inv.TotalIGST)
|
|
inv.GrandTotal = round(inv.GrandTotal)
|
|
|
|
return inv
|
|
}
|
|
|
|
func round(num float64) float64 {
|
|
return math.Round(num*100) / 100
|
|
}
|