Subversion Repositories SmartDukaan

Rev

Show changed files | Details | Compare with Previous | Blame | RSS feed

Filtering Options

Rev Age Author Path Log message Diff
37711 11 h 27 m amit /trunk/ Bulk-uploaded PO rows show the same availability breakdown as hand-picked ones

A row added by hand showed what the warehouse holds, what older orders have promised and how many
units the order could take; the same row arriving from a bulk upload showed none of it. The file
was the one place the numbers behind a quantity were hidden, which is the case where a mistake is
least visible and hardest to unpick afterwards.

describeAvailability now also answers for a list of items. Both reads it needs already took a
list, so a whole file costs the same two queries a single item does rather than two per row. An
item the warehouse holds nothing of is left out of the result instead of failing the upload - its
quantity is still checked when the order is priced, and the row simply shows no breakdown.

The single-item and batch paths build their answer from one shared method, so the two cannot drift
apart, and the screen reuses the renderer it already had.
 
37707 12 h 0 m amit /trunk/profitmandi-fofo/src/main/ GRN price mismatch: say which PO the invoice will be received against, and what to do if it closes first

Resolving a price mismatch discards the PO line and raises a replacement dated to the invoice. That
replacement is what the invoice is received against, so it is the one that has to stay open long
enough for the stock to arrive - and it closes on its own after four days for a movement, six for a
vendor order. Until now nothing said so: the GRN simply stopped matching and the correction sat
holding stock that had already arrived.

The confirmation now states what is about to happen and what to do if the stock lands late, and the
request screen explains what Po Id is. Reopening is offered only where it exists - a movement can be
reopened from Purchase Orders, a vendor order cannot and needs a fresh PO.
 
37705 12 h 2 m amit /trunk/profitmandi-fofo/src/main/ PO list: show the transaction id so a movement can be found from the invoice being received

An internal movement is raised as a transaction, so the invoice on the GRN desk leads straight back to its PO
through that id. The list is a DataTable, so having the column makes it searchable - which is how someone finds
the PO to reopen when a receipt arrives after auto-close, instead of hunting through date ranges.

Placed after Status so existing column positions are unchanged.
 
37704 12 h 3 m amit /trunk/ Reopen a movement PO whose stock arrived late, and stop stranding GRN price corrections

Internal movements auto-close after four days, which fits 99.6% of them - 5,491 of 5,515 receipts
land inside the window. The remainder leave the PO closed with the stock still in transit and
nowhere to receive it: 998 internal POs closed during 2026 still holding 21,996 unreceived units.

A closed movement PO can now be reopened from the purchase order list. Reopening stamps
reopenedAt, and auto-close measures from WarehousePurchaseOrder.getOpenSince() - reopenedAt when
set, the PO date otherwise - so a reopened PO gets the same fresh window a new one gets instead of
being closed straight back on the next sweep. Only movements between our own warehouses: an
external vendor PO that has closed is settled with that vendor, not reopened unilaterally.

Separately, a GRN price correction now checks that the PO it just raised is one the invoice can
actually be received against. Matching reads POs that are open and approved for the same supplier
and warehouse dated on or before the invoice; it never looks at the PO being corrected, so what
matters is that the new PO is receivable. 54 were not - backdated into INIT by the old approval
gate, hence outside the match - and each stranded silently: original line discarded, GRN completed
without it, the correction left holding a reservation for stock that had already arrived.

isOpen() names the open set - INIT, READY, PARTIALLY_FULFILLED - that the movement and commitment
queries already read.

Migration sql/add_po_reopened_at_20260918.sql adds reopenedAt, nullable and additive. It must run
before this ships: the entity maps the column.
 
37701 15 h 16 m amit /trunk/profitmandi-fofo/src/ PO create: expected stock counts toward the quantity, popover wording follows

Stock still to arrive is now part of what the order can take, so the breakdown reports it as a
qualifier on that number rather than as something set aside - 'Of these, still to arrive' instead
of 'On the way in (not yet received)', which read as though it could not be ordered.

Tests follow the two rule changes: expected receipts can be ordered against, a quantity beyond
what is held and expected together is still refused and says so, and availability counts expected
receipts toward what can move. 21 tests pass.
 
37699 17 h 25 m amit /trunk/profitmandi-fofo/src/main/ Fix: movement availability popover rendered empty except the closing note

The breakdown was built as a table. Bootstrap 3.4.1 sanitizes popover content against a
whitelist that includes neither table nor its rows and cells, and it removes a non-whitelisted
element together with everything inside it - so the whole table was dropped and only the note
survived, leaving a popover that referred to orders that were not shown.

Rebuilt from divs and spans, which are whitelisted. Sanitizing stays on rather than being
switched off for this popover: the content carries PO numbers and warehouse names read out of
the database.

Also stops the zero-quantity note pointing at orders that are not there - a warehouse whose
only stock is still inbound has nothing promised to point at. jsVersion bumped.
 
37697 17 h 36 m amit /trunk/profitmandi-fofo/src/ PO create: show what can move, and which orders hold the rest, while the quantity is typed

Picking an item on a movement order already called getPricing, which read the sending
warehouse's stock and returned only a price. It now returns the availability too, so the row
can show it: an info marker beside the quantity box opens the breakdown - in stock, promised
with each holding order named by PO number and date, anything still arriving, and the quantity
this order can take.

The quantity box flags the moment what is typed passes that cap, so it is corrected before
submitting rather than after being refused. The marker turns amber when stock is partly
promised and red when none can move.

Outside vendors are unaffected: their pricing response carries no availability and their rows
are left exactly as they were. jsVersion bumped so the screen picks up the new script.

Tests cover the four cases that matter: what can move with orders named, zero movable reported
rather than refused, this order's cap kept separate from what the warehouse holds when stock
came in at two costs, and a warehouse holding none of the item still refusing.
 
37670 1 d 19 h amit /trunk/profitmandi-fofo/src/main/ Check GSTIN shape on the onboarding screen before submit (jsVersion 425)

Both GST boxes are validated client-side and must match, so ops see the problem on the screen
instead of discovering it when NIC refuses the first invoice. Server-side guard in dao r37669
is what actually enforces it.
 
37643 2 d 22 h amit /trunk/profitmandi-fofo/src/main/ Hot Deal brand: admin edits attributes + optional OEM catalog mapping; brand/date/cap controls removed; jsVersion 424  
37632 3 d 2 h aman /trunk/ fix(loi): don't leave an LOI half signed when the signed PDF fails to save

Confirm Sign stores the partner OTP (/validateLoiOtp) before the browser builds and uploads the
signed PDF (/saveLoiDoc). When that second step failed - html2pdf not loaded from cdnjs, upload
rejected, tab closed, server error - nothing was shown, no LOI mail went out, no onboarding was
created, and because loiOtpPresent hid Generate LOI the filler could never re-sign (LOI 734).

- pendingFormList: loiOtpPresent only when OTP AND loiDoc are both saved; new loiSignIncomplete
flag shows a red "signed LOI not saved" note on the Pending LOI list (web + V2 app).
- loi-form.js Confirm Sign: every failure after the OTP says the LOI was NOT saved and how to
retry; checks html2pdf is loaded; success alert only after /saveLoiDoc confirms; no double submit.
- /saveLoiDoc (fofo + V2): refuses a document without a verified OTP; a repeat call for an
already saved LOI is a no-op (no second mail / completion).
- /validateLoiOtp (fofo + V2): clear message when no OTP was sent in 24h; reports the real
rejection reason (e.g. OTP already used) instead of always "wrong".
- sendSignedLoiPdfToPartner: filler without a manager no longer throws and rolls back the save.
- OtpProcessor.generateOtp: never hands back an already verified OTP under the 2-minute throttle.
- AppConfig version 423 for loi-form.js.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RhJD2sc6pf3Zd7f4hyGxhH
 
37630 3 d 3 h amit /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/ feat(mail): wire inactive-recipient filter and clean hardcoded addresses

Wire MailRecipientFilter into googleMailSender and gmailRelaySender. Remove inactive/unknown addresses from recipient and access lists, bulk uploader gate to akhil.kumar.
 
37620 3 d 23 h amit /trunk/profitmandi-fofo/src/main/ Offer creation suggests only active models unless asked for all

/entity accepted activeOnly but always searched with false, so the offer
screen's model picker listed every catalog of the brand, delisted ones included.
It now honours the flag. The default becomes false so the pages that never pass
it keep their current results; the notification product search, which already
asked for active-only, now gets it.

Each item-criteria block gets an Include inactive checkbox, off by default.
Ticking Exclude ticks it too: a brand-level offer still pays on delisted models
partners hold stock of, and they cannot be excluded if they cannot be picked.
Inactive models are labelled, and reloading the list keeps picks still present.

The reload flag is now per block; the old global one let a brand change in one
block be consumed by opening another, leaving the first with a stale list.
Brand names are URL-encoded so a brand like Ai+ is not sent as 'Ai '.

Requires profitmandi-common r37619 for the inactive label.
 
37593 6 d 21 h amit /trunk/profitmandi-fofo/src/main/ Show partners their aged Apple stock and why it lowers their credit limit

Apple handsets held past 30 days are deducted when the credit limit is
calculated, but nothing told the partner - they saw a limit drop with no
explanation. Adds a dashboard banner alongside the existing 'activated but not
billed' one, shown only when the partner actually has such stock (70 of 980
today), with the unit count, the value, and View Imeis for the detail.

- dashboard-aged-apple-stock.vm, parsed in dashboard1.vm under
#if($investments.aged_apple_qty > 0)
- count and value both read from the maintained partner_investment snapshot, so
the banner never runs its own query and always matches the amount the limit is
actually reduced by
- amount wrapped in <span class="currency"> so common.js formatCurrency() renders
it instead of a raw float
- /agedAppleImeis + aged-apple-imeis.vm list the units, excluding activated ones -
those are already deducted as activated stock and shown in their own banner, so
no handset appears in both
- click handler added to the existing activated-imeis.js rather than inline in the
fragment: an inline script in an AJAX-injected fragment re-registers
$(document).on on every load and stacks duplicate requests
- jsVersion 420 -> 421
 
37564 8 d 13 h amit /trunk/profitmandi-fofo/src/main/ Creation screens request only live listings from the item typeahead

/item takes an activeOnly flag (default false, preserving current behaviour)
and passes it to getAllPartnerItemStringDescription. getItemAheadOptions takes
an optional 4th argument, so existing 3-argument callers are unaffected.

Sent true from the two creation entry points:
- warehouse purchase (PO) invoice line picker
- the new-price-drop modal (#typeaheaditem)

Left false where the screen works on records that already exist and may since
have been delisted: the price-drop pause/manage picker (#typeaheaditem1),
catalog-item, combo and prebooking.

jsVersion 419 -> 420 to cache-bust common.js / warehouse-purchase.js (419 was
already taken by another change).
 
37559 8 d 22 h ranu /trunk/profitmandi-fofo/src/main/ ticket download option given and some enhancement on notification panel  
37543 10 d 4 h amit /trunk/profitmandi-fofo/src/main/ receive debit note: resolve the DOA section from the declared condition, not from a change

The condition dropdown renders with the partner's declared value already selected, so
a declared BAD - the ordinary DOA case - fired no change event and the DOA certificate
radios stayed hidden. The operator could not supply a certificate, and receiveDebitNoteItems
then refused the receipt for a missing one, making those debit notes unreceivable.

applyDeclaredConditions seeds each dropdown from the row's data-declared and settles the
dependent sections on load; the show/hide logic moves out of the change handler into
syncReceiveConditionState, which reads the current state of the form rather than whatever
was last touched. The change handler now calls the same function.

The server-side 'selected' stays as a fallback: without it a declared BAD would present as
GOOD if the script failed to load, and the server reads that difference as the warehouse
disagreeing with the partner, which rejects the whole debit note.

jsVersion 417 -> 418.
 
37526 14 d 22 h amit /trunk/profitmandi-fofo/src/ offer circular: scope config screen, resolve screen, web offer sync (fofo)

Screens (each its own endpoint, under the OFFER CIRCULAR menu):
- /offerCircularScope - add/edit divisions, take a brand in or out of scope,
register label aliases. 'Remove' is in_scope=0 + a required reason, never a
DELETE: offer.division_id is an FK and the history would go with it.
- /offerCircularResolve - the product queue, split out of the review screen. The
editor was a <td colspan=6> pretending to be a form, which is why it never
aligned; it is now master-detail. Naming and Coverage are separate tabs because
an alias cannot answer a bundle at all - the coverage panel says so and offers
the two answers that ARE safe (ignore, or reclassify as naming).

Ingest:
- ScopeConfig resolves division aliases and carries the canonical label on
Decision. insertOffer and ProductAliases.find use it; offer_raw_row keeps the
verbatim label, being the source of truth for re-parsing.
- CircularIngestRunner publishes to dtr.web_offer after the document is marked
PUBLISHED, in its own transaction with exceptions swallowed - a circular that
parsed correctly must stay published even if the web sync fails.

Review screen:
- the ingest summary was a raw Map.toString() inside a nowrap span and ran off
the card; now parsed into chips with the drop reasons behind a disclosure.

jsVersion -> 417 (merged with r37525's 412; cssVersion 53 kept from that commit).
 
37523 15 d 15 h ranu /trunk/ loi process added, revival and code changes process  
37513 16 d 21 h ranu /trunk/ notification live  
37493 17 d 3 h amit /trunk/profitmandi-fofo/src/main/ Fix: scheme item inline date edit fired N duplicate PUTs per click

scheme-details.vm is injected with .html(), which re-executes its inline
<script>, and a successful save re-injects it - so each load stacked another
$(document) handler for .edit/.cancel/.save-item-dates. One Save click then
fired N identical PUT /scheme/item/window, and PostInterceptor rejected all
but the first with 'Duplicate request.' (27 per click in prod today).
The stacked copies also carried a stale $scheme closure, so a save could
repaint the container with a previously viewed scheme.

Move the handlers to scheme.js (loaded once via include-scripts.vm) and read
the scheme id/window from data- attributes on the fragment root, keeping them
stateless. Fragment is now markup only.

Reuse cleanup while in here:
- configureMultiselect() replaces 3 near-identical multiselect configs
- loadBrandsByCategory/loadCatalogDescriptionByBrands take an optional
afterRender so the add-item modal reuses them instead of duplicating both
- toggleItemDateEdit()/initSingleDatePicker() collapse mirrored blocks
- SCHEME_DETAILS_CONTAINER single-sources the container id
- drop the template's duplicate toIsoDateTime (scheme.js already had it)
- updateSchemeItemWindow() now prefixes context like every other call

Bump jsVersion 409 -> 410.
 

Show All