Intranet · Store sales declaration & month closing
The monthly sales cycle for every store: a calendar of declared daily sales, manual entry, bulk XLSX import, estimated-sales declaration, month closing and validation, reopen requests, rejection with an automatic communication to the store, and an audit log of who changed what.
| Area | Intranet |
| Routes | intranet.sales · intranet.sales.index |
| Centre modules | intranet · intranet-sovos · intranet-yardi |
| Staff levels | root · admin · manager · scmanager · storeManager · commerce · owner · sales |
| Models | closed-month · commerce · estimated-sale · intranet-sale · intranet-sales-year · sale-concept |
| UI label | title.header.sales / menu.navbar.intranet.sale.index |
| Coverage | 7 use cases · 4 happy · 12 failure |
How it works
The most state-heavy part of the intranet. A store-month moves through four states (without_sales, unclosed, closed, validated) each with its own colour and icon. Role logic is deliberately split: canReopenMonth is true for admin/manager on a validated month, while canRequestToReopenMonth is the inverse role test for everyone else — so store users request and management grants. canAddNewSale combines the closed-month ability with ‘the month is not validated’, and the ability itself requires the month to be in the past, not already closed, and to have at least one sale. New-sale dates are clamped to the selected month (minAllowedNewSaleDate/maxAllowedNewSaleDate). Type guards are unusually strict: closeMonth, confirmCloseMonth and rejectCloseMonth all throw TypeError/IllegalStateError when given the wrong shape. Rejecting a close creates an intranet-communication addressed to the store, so the rejection is auditable and the store is notified in one step. parseXLSX reads the file through the xlsx service into draft sales for review before saveSales commits them, and downloadTemplate emits the expected shape. IntelliSales/Sovos centres hide the manual delete column and show Sovos totals instead (canShowSovosSales additionally requires a non-zero value).
Use cases
Why someone opens this (7)
- A store manager entering daily turnover for the month.
- Importing a month of sales from the till system as an XLSX instead of typing them.
- Declaring estimated sales when actuals are not yet available.
- A store closing its month so the centre can consolidate.
- Centre management validating or rejecting a closed month, with the store notified automatically.
- A store asking management to reopen a validated month to fix an error.
- Auditing who entered or changed a figure via the log table.
Path map
flowchart LR F(["Store sales declaration and month closing"]):::feat F --> H1["Month declared, closed and validated"]:::ok F --> H2["Bulk import from XLSX"]:::ok F --> H3["Close rejected and store notified"]:::ok F --> H4["Estimated sales declared"]:::ok F -.-> X1["Sale in a future date"]:::bad F -.-> X2["Sale in a closed month"]:::bad F -.-> X3["Amount missing or zero"]:::bad F -.-> X4["Date missing"]:::bad F -.-> X5["Month cannot be closed"]:::bad F -.-> X6["Close fails"]:::bad F -.-> X7["Reject-close fails"]:::bad F -.-> X8["Wrong argument types"]:::bad F -.-> X9["Invalid XLSX"]:::bad F -.-> X10["Estimated sales save fails"]:::bad F -.-> X11["API error with a detail key"]:::bad F -.-> X12["Month already validated"]:::bad classDef feat fill:#26688f,stroke:#26688f,color:#ffffff,font-weight:bold classDef ok fill:#e3f1ea,stroke:#0d6b42,color:#0d4a2e classDef bad fill:#f7e6e2,stroke:#9a2d1c,color:#6e1f13
Happy paths
Month declared, closed and validated
- Store user selects the store and month; the calendar shows the current state.
- Daily sales are added with date, concept, amount and ticket count — dates constrained to the month.
- When complete, the user closes the month; the confirmation modal names the store and month (
intranet.close_month-message).confirmCloseMonthvalidates the types and marks the month closed;informative-message.close-month.success.messageconfirms.- Management later validates it, moving the state to
validated.
Bulk import from XLSX
- User downloads the template, fills it and opens the import modal.
parseXLSXreads the file into draft sales shown for review.- User confirms;
saveSalespersists them andsales.upload-xlsx-successis shown.
Close rejected and store notified
- Management opens the reject-close modal on a closed month and writes a reason.
rejectCloseMonthvalidates the communication model and saves both the rejection and anintranet-communicationto the store.informative-message.reject-close-month.success.messageconfirms and the store sees the communication.
Estimated sales declared
- User with
declareEstimatesSalesopens the estimated-sales form.- The month’s start/end dates and the store are attached automatically.
- Save succeeds with
intranet.sales.estimated-sales-has-been-succesfully-saved.
Failure paths
Sale in a future date
Cause — Date later than today.
What happens —
error.sale.date.invalid_future(“You can’t add sales of future dates”).
Sale in a closed month
Cause — Target month already closed.
What happens —
error.sale.date.closed-month-error(“You can’t add sales in closed months”).
Amount missing or zero
Cause —
amountblank or exactly 0.What happens — Both a presence and a custom rule fail with
required+ amount — zero is explicitly rejected, not treated as ‘no sales’.
Date missing
Cause —
dateblank.What happens —
required+ date.
Month cannot be closed
Cause —
closed-month#canCreatefalse — month is current/future, already closed, or has no sales.What happens — The close action is unavailable; no message explains which of the three reasons applies.
Close fails
Cause — API error during closing.
What happens —
informative-message.close-month.error.message; the optimisticvalidated: trueis rolled back in the catch.
Reject-close fails
Cause — API error saving the rejection or its communication.
What happens —
informative-message.reject-close-month.error.message.
Wrong argument types
Cause —
closeMonth/confirmCloseMonth/rejectCloseMonthcalled with a non-Commerce, non-Month or non-IntranetCommunication.What happens — Throws
TypeError/IllegalStateError— developer-facing, surfaced as a crash rather than a message.
Invalid XLSX
Cause — Wrong format or unreadable file.
What happens —
sales.upload-xlsx-error(‘The file you have uploaded is not a valid format’);errorParsingMessageholds the parse detail.
Estimated sales save fails
Cause — API error.
What happens —
intranet.sales.estimated-sales-has-failedvia_estimatedSalesHasBeenFailedSaving.
API error with a detail key
Cause — The API returns
errors[0].meta['detail-key'].What happens — That key is translated directly into
errorMessage— a neat server-driven message channel, but it throws ifmetais absent.
Month already validated
Cause — Trying to add or delete sales in a validated month.
What happens —
canAddNewSaleis false and the delete column is not rendered.
QA checklist
Tick these off while regression-testing this feature. The boxes are plain Markdown, so progress is visible in git diffs.
- ✅ Month declared, closed and validated
- ✅ Bulk import from XLSX
- ✅ Close rejected and store notified
- ✅ Estimated sales declared
- ⛔ Sale in a future date
- ⛔ Sale in a closed month
- ⛔ Amount missing or zero
- ⛔ Date missing
- ⛔ Month cannot be closed
- ⛔ Close fails
- ⛔ Reject-close fails
- ⛔ Wrong argument types
- ⛔ Invalid XLSX
- ⛔ Estimated sales save fails
- ⛔ API error with a detail key
- ⛔ Month already validated
Implementation
app/controllers/intranet/sales/index.jsapp/controllers/intranet/sales.jsapp/routes/intranet/sales/index.jsapp/models/intranet-sale.jsapp/models/closed-month.jsapp/models/estimated-sale.jsapp/abilities/intranet-sale.jsapp/abilities/closed-month.jsapp/enums/shop-month-states.jsapp/components/intranet-sales-close-month.jsapp/components/close-month-modal.jsapp/components/reject-close-month-modal.jsapp/services/xlsx.js
Translation keys (24)
Key English title.header.salesSales management menu.navbar.intranet.sale.indexSales title.header.intranet-saleSales shopMonth.state.without_salesWithout sales shopMonth.state.unclosedOpen shopMonth.state.closedClosed shopMonth.state.validatedValidated intranet.close_month-messageYou’re going to close {shop}‘s selling registers of {month}. informative-message.close-month.success.messageThe month was closed succesfully. informative-message.close-month.error.messageAn error occurred while closing the month. Please try again later informative-message.reject-close-month.success.messageThe close month was rejected succesfully. A communication was sent to the store informative-message.reject-close-month.error.messageAn error occurred while rejecting to close the month. Please try again later sales.upload-xlsx-successXLSX has been uploaded with sucess! sales.upload-xlsx-errorThe file you have uploaded is not a valid format. Please, check the file and try again. sales.sale-conceptsSale Concepts sale.dateDate sale.amountAmount sale.conceptConcept sale sale.ticketsReceipts error.sale.date.invalid_futureYou can’t add sales of future dates error.sale.date.closed-month-errorYou can’t add sales in closed months intranet.sales.estimated-sales-has-been-succesfully-savedEstimated sales has been successfully saved intranet.sales.estimated-sales-has-failedSomthing has failed, please try again later communication.reject-close-month.subject{shopName} closing {month} rejected
Related
- Same area — Sales status overview & Yardi export · Sales & audit-log CSV download · Annual sales statement · Intranet communications (threads, confirmations, authorisations) · Communication subtypes · Files management · Footfall (flows) & Yardi upload · SAFT-E - IntelliSales import · Utility consumptions
- Shares data with — Customer receipts tab & ticket-stamp generation · Customer loyalty — coupon exchanges · Receipt verification queue · Store list, filters & exports · Store create & edit · Store CSV import · Activity editor (loyalty mechanics, rules & targeting) · Proposal editor (content, coupon, push, AI images)
- Behind the same module — Store create & edit · Footfall (flows) & Yardi upload · SAFT-E - IntelliSales import · Sales status overview & Yardi export · Annual sales statement
Generated from doc/feature-inventory.json · id intranet-sales · commit 276788ca6