Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Show changed files | Details | Compare with Previous | Blame | RSS feed

Filtering Options

Rev Age Author Path Log message Diff
37405 46 d 3 h amit /trunk/profitmandi-fofo/src/main/ Drive the onboarding/HR state dropdowns from inventory.statemaster

fofo-form.vm, fofo-edit.vm and hr_employee_form.vm each hardcoded their own state
list instead of using $stateNames. Six of those 37 values do not exist in the
master (& vs and variants, Uttaranchal, the pre-merger UTs), so anything picked
from them could not be resolved back.

Only the name="state" select changed in hr_employee_form.vm; its other dropdowns
are untouched.
 
37400 46 d 6 h amit /trunk/profitmandi-fofo/src/main/ Merge the three sale-invoice download endpoints into /invoice/download

generateInvoice, generateInvoices and downloadInvoices differed only in how they
resolved the order ids and who was allowed to ask; everything after that was the
same. That drift meant only the single-order download stapled the policy
certificates, so the same invoice pulled from sale history came out without
them. One handler now selects by orderId, partner date range (admin only) or the
caller's own sale-history search, over reusable resolvers plus a shared
render-and-respond step. The old URLs stay as deprecated shims.

The admin range download is now fault tolerant - one unbillable order used to
fail the whole batch.

Remove the commented-out thermal variant of generateInvoice and the unused
paymentOptionIdPaymentOptionMapUsingPaymentOptions. Bump the asset version for
the sale.js change.
 
37390 47 d 9 h ranu /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ total show on warehouse wise stock value on item detail  
37389 47 d 9 h ranu /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ total show on warehouse wise stock value on item detail  
37385 47 d 13 h vikas /trunk/profitmandi-fofo/src/main/ Added Notice and PJP access to akhil.kumar@smartdukaan.com  
37380 50 d 8 h amit /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ Fix /saleDetails 500 on insurance-only orders and null billing name/phone

Two defects on the sale-details screen, both from a legitimately-empty source.

1. /saleDetails 500s on an insurance-only order. A policy is not a catalog item,
so an insurance sale carries no fofo_order_item row; itemIds comes out empty and
itemRepository.selectByIds hits GenericRepositoryImpl.selectAllByInOrderByDesc,
which throws "List should not be empty" to guard against an empty IN (). The
same guard already exists two methods below at the sale-search site, so this
carries it across to the detail site. 346 orders across 95 partners are affected
- every insurance-only order ever written. The screen already loads the policies
and the view renders them, so nothing else was needed to display the sale.

2. Billing name and phone rendered as the literal
.getName(). A plain POS sale does not require an
address (only insurance does), so ~140k orders across 1038 partners carry
customerAddressId 0 and the lookup returns null. The existing guard covered the
address *string* but the object was added to the model unconditionally, and
Velocity prints an unresolvable reference verbatim. Fall back to the customer's
own name and mobile, which the invoice PDF already does via
OrderServiceImpl.createCustomAddressWithoutId - so screen and invoice now agree.
The fallback is transient and never persisted; a missing customer row is
swallowed deliberately, since this feeds a display field and throwing would turn
blank text into a 500.

Applied at both model-writing sites in this controller.
 
37378 50 d 12 h amit /trunk/profitmandi-fofo/src/main/ Put the action-pending returns at the top of Sale Returns, and filter by partner

Sorting the received list pending-first changed nothing on screen, because every return
that still needs an action was missing from it. The list was built from purchase return
orders inside the date window, and a return order only exists once the warehouse has
received the goods - so a debit note awaiting receipt had no row to sort, and a return
sitting unrefunded for weeks fell out of the window entirely.

The screen is now two lists:

- Action Pending, on top, with no date bound. It merges the three shapes a pending return
takes - a debit note never received, a return order received but unrefunded, and one
rejected but not yet acknowledged - into a single row type, oldest first, with the age
in days beside it. Each row carries only the action that actually applies to it
- Settled Returns below, refunded and cancelled only, still bound to From/To. The pending
rows were lifted out of it, so nothing is listed twice

Two things worth recording:

- a debit note has no warehouse of its own. It is placed through the item's invoice and
the order that invoice was raised on, and a note that cannot be placed is dropped rather
than shown to a warehouse it may not belong to
- debit notes raised before the receive/refund flow existed were settled the old way and
cannot be worked from this screen. The cutoff is read from the earliest return order in
the database, so it needs no maintenance, and the page says so in a footnote instead of
quietly hiding them

The partner filter reuses the shared /partners typeahead and passes the id down into both
queries. Only a picked suggestion filters, so a half-typed name cannot blank the page. Its
handlers sit inline in the template, next to the markup they drive, following the pattern
add-wallet-request.vm already uses - which leaves the now-unused #invoice-return-date-apply
handler in return.js dead, to be removed with the return.js work already in flight.

Rendered offline through Velocity with the app's own directive.set.null.allowed to confirm
all four action variants emit the right buttons.
 
37374 51 d 7 h ranu /trunk/ total show on warehouse wise stock value on item detail  
37355 52 d 13 h aman /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ AI lead intake (fofo, live): assign new leads to a random active BGC L1 instead of the hardcoded Khushbu/Archana round-robin  
37348 53 d 8 h ranu /trunk/ super retailer club 5 live  
37346 53 d 10 h amit /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ Sort sale returns so the ones still needing an action come first

The Recently Received Debit Notes table was ordered purely by receive time, so a
return waiting on a refund or on the partner's acknowledgment sank below rows that
were already refunded and needed nothing. On a busy warehouse the actionable rows
were off the first screen.

- recentOrders is now ranked pending-first (Received - Pending Refund, Rejected -
Pending Acknowledgment) ahead of settled ones (Refunded, Cancelled), with the
existing receiveTimestamp DESC kept as the tie-breaker inside each group
- getPendingActionRank follows the same precedence invoice-return.vm uses to pick
the status badge - reject checked before refund - so the ordering can never
disagree with the label the user sees
- ranked on the return's own state, not on the viewer's canReceive/canRefund
permissions, so a pending return stays at the top for everyone looking at it

Presentation only; no query, entity or lifecycle change. Note that returns never
received at all cannot surface here regardless, since the query filters on
receiveTimestamp BETWEEN the selected dates.
 
37345 53 d 10 h vikas /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ Beat Journey (today): LOI-based 'Onboarded' + last-billing recency board columns, level-filtered orders list (grouped by partner), and flag tuning — remove #1/#3, use per-visit total_distance for #4.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
 
37343 53 d 11 h vikas /trunk/profitmandi-fofo/src/main/ Update PJP, Visit quality flags  
37337 53 d 13 h amit /trunk/profitmandi-fofo/ Move offer-circular ingest out of cron and into the portal

The parse now runs in profitmandi-fofo, on a background thread, triggered by the
upload that produced the document.

Why: splitting one feature across two artifacts with independent deploy cadences
cost a full day. fofo shipped, cron did not, and a valid upload sat in DRAFT with
nothing on the server able to parse it - the deployed cron jar contained none of the
ingest classes. One 9-page PDF a month never justified a batch tier, and the portal
already ships two PDF stacks, so the isolation argument for keeping PDFBox out was
weaker than it looked.

- 13 parser classes move verbatim from com.smartdukaan.cron.offercircular to
com.spice.profitmandi.web.offercircular. No logic changed.
- CircularIngestScheduler becomes CircularIngestRunner: the @Scheduled(every 5 min)
entry point and the offer.circular.ingest.enabled flag are gone, replaced by a
single-threaded daemon executor. All claim, ingest and notification logic is
unchanged.
- Upload hands the document id to the runner AFTER COMMIT, not inline. The DRAFT row
is written inside the request transaction; a worker starting immediately would race
that commit, find nothing to claim and silently do nothing - which is precisely the
stuck-on-DRAFT symptom this change removes.
- The guarded claim is KEPT even though there is now one trigger. It still stops a
double-submit, a second portal node, and a re-ingest racing an in-flight parse.
- Re-ingest parses immediately instead of queueing for a scheduler.
- The stall reaper runs when the review screen loads. There is no timer here any
more, and a document stranded by a redeploy mid-parse only matters when somebody
looks for it - which matters more now the parse lives in the web application.
- tabula moves to this module with its exclusions intact, as does the
dumpCircularClasspath helper the local ingest harness depends on.
- Screen no longer claims "the ingest job runs every 5 minutes", which was untrue the
moment cron stopped being the route; poll interval 15s -> 3s to match a parse that
takes seconds. jsVersion 404 -> 405.
- offer.circular.review.url added here, since the runner sends that email now.

Verified: full ingest of the Aug'26 circular through the relocated code is identical
to the reference - 239 offers, 663 products, 279 AUTO_EXACT, 361 benefits, 511
tenures, 911 bank links. ProductNamesTest 11/11 in its new home.
 
37332 53 d 15 h amit /trunk/profitmandi-fofo/src/main/ Move the offer-circular upload directory off a developer home path

Every upload in production failed with a 400 on mkdirs. offer.circular.dir was
never set in any environment, so it fell back to the code default
/users/amit/uploads/offer-circulars; Tomcat runs as the tomcat user, which cannot
create /users at the filesystem root, and the controller threw "Circular storage
directory is not writable".

- default is now /var/lib/smartdukaan/offer-circulars, a real server path
- offer.circular.dir set explicitly in prod.properties, and pointed at the
developer's own space in dev.properties, since /var/lib needs sudo on a
workstation
- staging.properties deliberately untouched; the new default covers it

The location has two hard constraints, now recorded on the field itself:

- OUTSIDE webapps/. A redeploy wipes anything under it, and
offer_document.stored_path is how both re-ingest and re-download resolve the
PDF, so a wipe breaks them permanently. Stray PDFs are already sitting in
WEB-INF/classes/META-INF on the server from exactly this mistake.
- OUTSIDE any web root. A circular carries the OEM's confidential cashback
economics and must never be servable as a static file. Hence /var/lib rather
than /var/www, whose local precedent /var/www/partner_stats is chmod 777.

Only fofo needs the key: the cron ingest resolves the PDF from
offer_document.stored_path and reads it as root, so 0750 tomcat:tomcat is enough.

Deploying this alone does not fix production - the directory must also exist and
be writable by tomcat.
 
37331 54 d 2 h amit /trunk/profitmandi-fofo/src/main/ Add offer circular review and curation screen

Shows the nine verbatim PDF columns beside what the parser made of them, uploads a
new monthly circular, and resolves the products the matcher could not.

Upload deliberately does not parse - it stores the PDF, sha256-deduped, and
registers it DRAFT, so an upload can never half-populate the offer tables. The cron
ingest job picks it up from there.

The curation queue groups by distinct (division, raw text) because that is the
product_alias key: one answer clears every row carrying that text, on this circular
and on every later one. On the Aug'26 circular that turns 152 unresolved rows into
106 decisions. Three actions mirror product_alias.action - PIN to a catalog id,
REWRITE to the catalog's spelling and match again, or IGNORE.

- Writes product_alias only, so nothing the screen does can set manually_curated
and lock a circular against re-ingest. Coverage decisions, which do need that,
are listed but visibly parked rather than offered a naming answer.
- A saved decision changes nothing until re-ingest, because aliases are read at
ingest time; the queue says so and the re-ingest button puts the document back
to DRAFT for the scheduler. The portal cannot run the ingest itself - that lives
in profitmandi-cron, which fofo does not depend on.
- The closest rejected match is shown for context and never pre-selected. Four new
OnePlus models each "nearly" matched a 2015 OnePlus 2 at 0.80-0.84, and offering
that as a suggestion invites a reviewer to confirm it. Where the catalog has no
such SKU the search says so and steers to IGNORE.
- aliasExport emits every decision as replayable SQL, because the portal writes to
one database and environments would otherwise drift apart silently.

Access is an email allowlist AND the admin role. Both are enforced in the
controller; the sidebar entry repeats the allowlist because visibility in auth.menu
is role-driven and cannot express a per-email rule.

No jsVersion bump: offer-circular-review.js is a new file that was never cached,
so it needs no cache-buster, and churning that shared counter only conflicts with
whoever is mid-edit on it.
 
37327 54 d 9 h ranu /trunk/ super retailer club 5 live  
37324 54 d 10 h aman /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/controller/ Lead Management: restrict lead view to field-level sales only (L1-L3). Any SALES position at any level previously forced the own+reportees lead filter, so senior sales heads (L4+ BM/RSM/NSM/co-founder L7) lost visibility of all leads and their dashboard charts collapsed. hasCategory check replaced with isFieldSales() at both view-filter sites; assignee validation untouched.  
37319 57 d 8 h amit /trunk/ Fix partner-performance tertiary: aggregate order items (qty*mop), not the POS-typed order header

The tertiary panel summed fofo_order.total_amount - a price typed at the partner POS
and never validated against the catalogue - and attributed each whole order to its
first line item's brand via .get(0). One mistyped digit inflated reported sell-out
10x, and mixed-brand orders booked 100% to the first brand, leaving the rest at zero.
Defect dates from r32000/r32034 (May 2023); the panel was the only tertiary consumer
diverging from the qty*mop basis.

- New FofoOrder.selectMonthlyBrandTertiary: sum(quantity*mop) grouped by
(year*100+month) and item brand - the same basis selectPartnerTertiarySales
already uses for the DSR and the partner tier calculation
- PerformanceController: replaces two entity-loading queries (every FofoOrder and
FofoOrderItem for 6 months) with one aggregate; all items now count, each under
its own brand
- Month labels unchanged - toMonthLabel rebuilds the MMM''uu key, template untouched
- Remove V2FofoPerformanceController, the /v2/fofo JSON copy carrying the same defect

Verified against dev DB for fofo_id 175139501: Jul 2026 now 16,94,939 (panel
previously showed 50,92,440); all six months match the qty*mop basis.
 
37312 57 d 14 h ranu /trunk/ one assist ew at 99 up to 20k  

Show All