Support #10628 ยป implementation_plan.md
Implementation Plan: Request Update to Quote Auto-Recalculation
This plan establishes a dual-execution pure calculation engine that synchronizes Request updates with linked Quotations headlessly in the backend without requiring UI interaction, while keeping real-time interactive calculations 100% client-side in the frontend (preventing chatty API calls on discount/value changes).
1. System Overview & Realtime Schema Values
Current Flow vs Target Architecture
sequenceDiagram
autonumber
participant UI as Frontend Quote / Request UI
participant RS as Request Service (Backend)
participant CE as Quote Calculation Engine
participant QS as Quotation Service (Backend)
participant DB as MongoDB Database
Note over UI,CE: Flow 1: Interactive Frontend Quote Creation (0ms Latency)
UI->>CE: Local in-memory calculation on every keystroke (mathjs)
UI->>QS: Save Quote with Commercial Snapshot (summaryType, discount, tax, deposit)
QS->>DB: Persist Quotation & CostBreakdowns
Note over RS,DB: Flow 2: Headless Request Update (No UI Interaction)
UI->>RS: PUT /api/v2/request/updaterequest/:requestId (Area/Products/Labor changed)
RS->>DB: Update Request document
RS->>QS: Trigger syncLinkedQuotations(updatedRequest)
QS->>DB: Find active/draft Quotation for requestId
QS->>CE: Recalculate costBreakdowns, subTotal, tax, discount & totals using Quote's saved commercial rules
QS->>DB: Save updated Quotation & sync Invoice deposit if applicable
RS-->>UI: Return updated Request & sync summary
Real-Time Database Schemas Involved
1. RequestSchema (models/MVP@2/request-model.js)
requestId: Unique identifier (e.g.,R_0001).selectedService[]:serviceId: ObjectId referencingService.unitType:"area"|"quantity"|"time"|"none".areaInputType:"dimension"|"manual".length,width,area,units: Dimensional measurements.marginType:"percentage"|"amount".marginValue: Number.selectedProductOptions[]:productId: String.productData: Object (contains price, name, etc.).fields: Map/Object of formula parameters (e.g.{ "area": "500", "depth": "3" }).selectedEquipment[]:equipmentId: String / ObjectId.equipmentData: Object (cost, rate, hours, quantity).selectedLabor[]:laborId: ObjectId.laborData: Object (payment/rate, hours, quantity).siteVisitStatus:"Completed"|"Scheduled"|"InProgress"|"NoSiteVisit"|"NotScheduled".
2. QuotationSchema (models/EvergreenQuotation/EverGreenQuotation.js)
requestId: String linking to Request.quotationNumber: String (e.g.,Q_0001).status:"drafted"|"waiting for approval"|"created"|"accepted"|"inProgress"|"completed".- Commercial Policy Rules:
IsBaseprice: Boolean (true= Base Price Summary,false= Actual Total Summary).discountType:"percentage"|"amount".discountAmount: Number.excludeTax: Boolean (true= tax exempt / excluded,false= standard tax).depositType:"percentage"|"amount".depositPercentage: Number.requiredDeposit: Number.miscellaneousCosts[]:[{ service: ObjectId, cost: Number, remarks: String }].
- Dynamic Totals:
subTotal,actualSubTotal: Number.taxAmount,actualTaxAmount: Number.total,actualTotal: Number.costBreakdowns[]: Array ofcostBreakdownSchemawith per-service products, labor, equipment, and margin rollups.
2. User Review Required
[!IMPORTANT]
Zero Keystroke API Calls Maintained
In the frontend (useNewQuotation.tsxandNewQuotationPage.tsx), all real-time calculations on discount changes, tax toggles, and radio selections remain 100% local in-memory execution viamathjs. No API requests will be triggered on input typing or discount adjustment.[!WARNING]
Quotation State Locking Policy on Request Update
When a Request is updated:
- Draft/Pending Quotes (
drafted,created,waiting for approval): Will be automatically recalculated and saved.- Approved Quotes (
accepted,completed): Will NOT be automatically altered to protect legal/billing contract integrity; instead, an audit record will log that the underlying Request changed.
3. Proposed Changes
Component 1: Shared Pure Calculation Engine
[NEW] [quoteCalculationEngine.js](file:///c:/thriveni/folder%20f/POS/EGF_POS/evergreen_pos_be/src/utils/quoteCalculationEngine.js)
A pure mathematical calculation module in Node.js (utilizing mathjs) that mirrors the frontend logic:
calculateProductQuantityAndCost(service, productOption, productCatalog): Evaluates formula usingmathjsagainst fields (e.g.(area * depth) / 324).calculateResourceCosts(service, laborMap, assetMap): Calculates labor and equipment totals.getServiceMarginAmount(service, subtotal): Computes percentage or flat amount margin.calculateQuotationFromRequest(request, existingQuotation, catalogData):- Computes
basePriceSubtotalandactualTotalSubtotal. - Determines active subtotal based on
existingQuotation.IsBaseprice. - Applies
discountTypeanddiscountrate. - Applies
taxPercentage(or $0$ ifexcludeTax === true). - Computes
finalTotal,requiredDeposit, and updatescostBreakdowns[].
- Computes
Component 2: Backend Request & Quotation Services
[MODIFY] [request.service.js](file:///c:/thriveni/folder%20f/POS/EGF_POS/evergreen_pos_be/src/services/nursery/request.service.js)
- In
exports.updateRequest:- After
Request.findByIdAndUpdatesaves the updated request, add a hook call toquotationService.syncQuotationsForRequest(dbName, updatedRequest, id). - Ensures changes to area, dimensions, selected products, labor, and equipment immediately cascade to linked non-finalized quotations.
- After
[MODIFY] [quotation.service.js](file:///c:/thriveni/folder%20f/POS/EGF_POS/evergreen_pos_be/src/services/quotation.service.js)
- Implement
exports.syncQuotationsForRequest(dbName, requestDoc, userId):- Finds all non-finalized quotations where
requestId === requestDoc.requestIdandisDeleted !== true. - Resolves required Service, Product, Asset, and Labor catalogs for the new service items.
- Calls
calculateQuotationFromRequest(...). - Writes new totals (
subTotal,taxAmount,total,actualTotal,costBreakdowns) to the quotation. - Appends an audit entry in
actionHistorystating:"Quotation automatically recalculated due to Request update". - If linked invoice exists (
invoiceId), callspersistInvoiceWithDepositSync.
- Finds all non-finalized quotations where
Component 3: Frontend Quotation Persistence & Local Engine Alignment
[MODIFY] [NewQuotationPage.tsx](file:///c:/thriveni/folder%20f/POS/EGF_POS/evergreen_pos_fe/src/pages/Quotes/createQuote/NewQuotationPage.tsx)
- Ensure the payload sent to
/Quotation/create-quotationor/Quotation/update-quotationexplicitly saves the full commercial snapshot:IsBaseprice: summaryType === 'basePrice'discountType:"percentage"|"amount"discount: numeric valueexcludeTax: booleandepositType:"percentage"|"amount"depositPercentage: numeric valuemiscellaneousCosts: array of service misc items
- Keep the in-memory
mathjscalculation reactive in React state with zero latency.
4. Verification Plan
Automated / Manual Verification
- Formula & Calculation Verification:
- Create a Request with Service unit
area = 500 sqft, Product with formula(area * depth) / 324withdepth = 3. - Create a Quote with 10% discount and 8% tax.
- Create a Request with Service unit
- Headless Request Update Test:
- Update the Request area from
500to1000 sqftviaPUT /api/v2/request/updaterequest/:requestId. - Fetch the Quotation via
GET /api/v1/Quotation/getQuotationById/:idwithout touching the UI. - Verify that:
- Product quantity doubled in
costBreakdowns[0].products[0].quantity. subTotal,discountAmount,taxAmount, andtotalare accurately updated.actionHistorycontains the automatic recalculation entry.
- Product quantity doubled in
- Update the Request area from
- Frontend UI Interaction Test:
- Open the Quote creation page and type discount values (
1,5,10,15). - Monitor Network DevTools tab: Ensure 0 network API requests are fired while typing.
- Open the Quote creation page and type discount values (