package gst 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 }