Subversion Repositories SmartDukaan

Rev

Go to most recent revision | Show changed files | Directory listing | RSS feed

Filtering Options

Rev Age Author Path Log message Diff
37470 38 d 10 h amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ Remove dead Itel/Tecno SAP activation services

Both services call SAP OData endpoints that Transsion decommissioned on 2024-11-20:

ItelImeiActivationService -> cms.itel-mobile.com:8099/.../ZTERTIARY_SALES_REPRT_SRV
TecnoImeiActivation -> cms.tecno-mobile.com:8099/.../ZTERTIARY_SALES_REPRT_SRV

Both hosts now refuse connections outright (verified from the prod app server).
That decommissioning is what stopped Itel and Tecno activation ingest within the
same hour on 2024-11-20; everything since has arrived via manual CSV upload.
Neither service has produced a row in 21 months and neither can again.

Removed:
- both service classes
- ScheduledTasks.checkItelImeiActivation / .checkTecnoImeiActivation, their
@Autowired fields and imports (the only callers)
- the --checkItelImeiActivation / --checkTecnoImeiActivation startup args

Unaffected: ItelImeiActivationNewService and checkItelImeiActivationNew, which
target the current imwav portal and were fixed in r37467.
 
37469 38 d 12 h ranu /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ rbm drr dashboard , all today po rbm view maped  
37468 38 d 13 h ranu /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ rbm drr dashboard new ui looks  
37467 38 d 15 h amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/itelImeiActivation/ Fix Itel/Tecno activation pull: live session tokens, correct credentials, per-account isolation

The imwav pull has never worked since it replaced the SAP feed. Three defects:

- Login response was discarded and the listing call sent p-auth/utoken values
captured by hand in Jul-2023, so every request went out with an expired session.
Now takes utoken/urToken from the login response.
- Itel (IN404338I) carried a stale password (Dukaan@1234), which is why its pull
started failing Feb-2026. Corrected, and the two missing Tecno branch accounts
(Noida IN411226T, Lucknow IN404339T) added - the code previously knew only
IN404338T, which has no enterprise scope and returns nothing.
- Existing rows were looked up by the activation date string instead of the IMEI.
serial_number is the primary key, so the lookup always missed and re-persisted a
row that already existed, colliding on duplicate key on any re-pull.

Also pulls each account independently so one dead login cannot abort the remaining
accounts, and fails loudly when the portal rejects a login or the report call
instead of proceeding into a confusing downstream error.
 
37466 38 d 15 h amit /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ Show aged Apple stock on the partner Total Investment breakdown

Adds an 'Apple Stock More Than 30 Days' row below 'Unbilled Activated Stock'
so the reason a partner's suggested credit limit dropped is visible. Display
only -- the figure is not deducted from the other rows.

Value comes from partnerStat.tmp, so it reads 0 until the cron that rebuilds
that file has run against the new build.
 
37465 38 d 15 h amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ Exclude aged Apple stock from investment when suggesting partner credit limits

Apple handsets a partner has held more than 30 days from GRN no longer count
toward the investment that drives the suggested limit. Applied in
getSuggestedAmount via getCreditableInvestment, covering the SDDIRECT,
hundred-percent and SIDBI branches. The aged amount is logged alongside each
limit change so a drop can be traced.

Scoped to the limit calculation only -- getTotalInvestment() is untouched, so
checkout payment options, the investment-OK gates and partner-facing screens
see the same stock value as before.

Measured on prod at time of commit: 580 units / ~3.94 Cr across 88 partners.
 
37464 38 d 15 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/ Track aged Apple stock on partner investment; add category filter to stock value lookup

selectPartnerStockValueMap now takes a nullable categoryId so the same query
serves both the existing Live Demo exclusion (null = all categories) and the
new Apple handset lookup (category 10006). Existing callers pass null and are
behaviourally unchanged.

PartnerDailyInvestment carries agedAppleStockAmount, populated in both the
single and batch investment paths. It is JPA-@Transient, so no schema change,
and it still travels through the partnerStat.tmp serialization the FOFO
investment screen reads. Deliberately not part of getTotalInvestment() -- the
deduction is a credit-limit policy, so stock value is unchanged for checkout,
the investment-OK gates and partner-facing screens.
 
37463 38 d 15 h amit /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/model/ Add aged-stock constants for Apple credit-limit haircut

Apple handsets held by a partner beyond AGED_STOCK_APPLE_DAYS (30) days
from GRN are excluded from the investment used to suggest a credit limit.
 
37462 38 d 15 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/event/ Fire TagListing Solr update after commit instead of inside the write transaction

TagListingChangeListener used a plain @EventListener, so the async Solr rebuild
ran on a separate thread in a new transaction while the publishing controller
transaction was still open. FofoSolr.updateSingleCatalog then re-read
tag_listing on a connection that could not see the uncommitted rows and
re-indexed the pre-change price; for a catalog's first-ever listing it read
zero rows, took the isEmpty branch and deleted the doc from Solr.

Switched to @TransactionalEventListener(phase = AFTER_COMMIT). All publish
sites (TagListingController, PriceDropController, IndentController,
InventoryController, ModelHotDealServiceImpl) are inside a @Transactional
boundary, so no event is dropped.
 
37461 40 d 10 h ranu /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/ rbm drr dashboard new ui looks  
37460 40 d 10 h ranu /trunk/ rbm drr dashboard new ui looks  
37459 40 d 12 h amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/scheduled/ Motorola IMEI activation: secondary + tertiary in one browser session

Mirrors the oppo/realme combined jobs. MotorolaImeiActivationService drives
CheckMotorolaWarrantyTask, with MotorolaChallengeSolver for the challenge.

Cadence comes from the pool query, which defers an imei for `days` after each
attempt (saveActivation bumps createTimestamp even when no date came back), so
days=2 retries everything every two days. Pending pool measured 1,534
(1,182 secondary + 352 tertiary); at ~10-14s/imei, 60 per invocation is about
12 minutes of driver time and clearing the pool inside 48h needs roughly 26
invocations, i.e. an OS cron entry every 90 minutes.

Do NOT schedule it inside the oppo/realme window: each driver tree costs
~850MB and this box has been OOM-killed twice with tomcat the victim, so peak
concurrent drivers is the number that matters.
 
37458 40 d 13 h aman /trunk/profitmandi-fofo/src/main/ LOI payment screenshot: report upload failures instead of reporting the field as missing

Submit validated the hidden #payment-sc-docId, but a file input keeps displaying the
chosen filename regardless of what happens next - so a failed /document-upload left the
field looking attached to the operator and missing to the validator ("The following
fields are required: Payment screenshot" on a form that was in fact filled in).

The failure was silent three ways: doAjaxUploadRequestHandler had no error handler; the
global ajaxError reporter calls bootbox, which loads from cdnjs and throws if that is
blocked; and the confirm('Confirm file upload ?') gate returned false with no trace once
Chrome's "prevent this page from creating additional dialogs" box was ticked.

- common.js: optional error callback on doAjaxUploadRequestHandler (additive, opts out of
the generic global dialog via suppressGlobalError); uploadErrorMessage() extracts the
server's real reason from the JSON body or the plain-text duplicate-request body;
uploadDocument() pre-checks size/type, verifies a document_id actually came back, and
reports a message naming the file - .HEIC and .webp get specific guidance;
showAlert() falls back to native alert when bootbox is absent.
- loi-form.js: drop the confirm() gate, 'input' -> 'change', and clear the file input
whenever the id does not land so the widget can never claim an unuploaded file. Status
line under the field (Uploading... / Attached: name / Not attached). Submit now
distinguishes not-picked, still-uploading and upload-failed. Same treatment for the
OnboardingDocumentTable and mk_brand-fee-collection-details uploads, and the brand-fee
dialog names the Attachment field instead of echoing the raw form key.
- loi-form.vm, pendingForm.vm: accept="..." on both file inputs plus the status span.
- FileUploadController: reject empty files, report actual size against the actual limit,
and resolveExtension() falls back to the filename when the browser sends a generic MIME
type (Android file managers send application/octet-stream for ordinary JPG/PNG/PDF).
- AppConfig: static resource version 395 -> 396 for the JS change.
 
37457 40 d 18 h amit /trunk/profitmandi-fofo/src/main/ Lock the KYC state to the GSTIN on the LOI

The LOI already captures and validates a GSTIN, and the first two characters of a
GSTIN are its GST state code - so once the LOI exists the KYC state is a fact
about that number, not something to re-type. When one is present the state
dropdown is prefilled from it and locked; otherwise it stays a normal
master-backed select.

Derived from the GSTIN rather than loi_form.gstState: that column is typed by
hand and holds values like 'UP', 'UTTAR PARDESH' and 'MADHAY PARDESH' which
resolve against nothing, and it is blank on 3 rows. Checked against prod - all
693 GSTINs on record are 15 characters and every one resolves through
statemaster.stateCode, and in all 14 rows where the two disagree the GSTIN is
right and the typed value is wrong or empty.

The lock is per row, not per page: the KYC screens list many onboardings, so the
derived state is passed as a map keyed by onboarding id. Blank rows carry their
onboarding id so a new KYC entry is locked too, which is the case that matters.

data-gst-locked keeps the row's dblclick edit toggle from re-enabling the field;
the value still submits because the JS reads it explicitly with .val().
 
37456 40 d 18 h amit /trunk/ Block billing when NIC rejects the transporter GSTIN for e-way bills

NIC files the e-way bill alongside the IRN, so a deregistered transporter
GSTIN returns Status=1 with InfoDtls[InfCd=EWBERR] (3029 "GSTIN - ... is
not active"): the IRN is valid while ewb_no stays null. Nothing downstream
reads that as a failure, so invoices kept being issued for goods that
could not legally move.

Cache the rejected transporter GSTIN in Redis and refuse to bill through
it. The block is keyed on the GSTIN, since one GSTIN is shared by several
warehouse_provider rows, and it carries the day it was raised so it lapses
at midnight and each new day re-tests NIC once. Correcting the GSTIN in
the provider panel lifts it immediately.

Only errors that are the transporter's fault block billing - NIC's 3029,
or any message naming the GSTIN we sent as TransId. Every other EWBERR
behaves as before: the IRN is filed and the e-way bill is retried later.

Guard sits in addBillingDetailsForGrouppedOrders before the pessimistic
lock and before any mutation, mirroring LogisticsServiceImpl#getEwbDetails
(order's own warehouse; self-pickup and runner skipped, as they travel on
a vehicle number rather than a transporter id).
 
37455 40 d 19 h amit /trunk/profitmandi-fofo/src/main/ Make the KYC state a master-backed dropdown instead of free text

fofo-kyc-row.vm and legal-kyc-row.vm captured state as a free-text input, so
nothing stopped a spelling the master does not hold. That state is resolved back
through inventory.statemaster by name later, and an unresolvable one cannot be
turned into a state code. Both now render a $stateNames select, and all five
render paths in PartnerOnBoardingPanelController supply the list.

panel-listing.js reads these fields positionally, by td index and
input[type='text']. A select does not match that selector, so the value would
have come back undefined and KYC would have saved a blank state silently -
both selectors now target the select. Row editing is unaffected: the enable
toggle uses :input, which covers select.

Bump the asset version for the panel-listing.js change.
 
37454 40 d 19 h amit /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/ Build googleMailSender as AuthenticatedIdentityMailSender

The bean authenticates as sdtech@smartdukaan.com, so Google Workspace only permits
that address in From. Callers hardcode noreply@, which Gmail refuses with 535
'Authenticated user is not authorized to send mail', silently dropping every alert
sent through it. The sender now rewrites From to its own username at send time.

See profitmandi-common r37451.
 
37453 40 d 19 h amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ Build googleMailSender as AuthenticatedIdentityMailSender

The bean authenticates as sdtech@smartdukaan.com, so Google Workspace only permits
that address in From. Callers hardcode noreply@, which Gmail refuses with 535
'Authenticated user is not authorized to send mail', silently dropping every alert
sent through it. The sender now rewrites From to its own username at send time.

See profitmandi-common r37451.
 
37452 40 d 19 h amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/integrations/gstpro/ Fix e-invoice IRN failure when the NIC auth token is about to expire

NIC reissues the same auth token until it genuinely expires, so evicting the cache
inside the 5 minute leeway hands back the identical near-dead token. getAuthenticatedHeaders
checked the leeway once, before that re-fetch, and never inspected what came back.
NSUPHR1770 filed with a token that had 12 seconds of life left and the gateway answered
412/GSP752 'eInvoice AuthToken not found or expired'; the invoice filed a minute later,
past the expiry and so on a genuinely new token, succeeded.

getUsableAuthToken re-checks the refetched token and, when it is still inside a 60s
floor, waits out the remainder before minting again. The wait is bounded by that floor
and every caller on this path is a cron/async thread already running against a 60s NIC
timeout with no transaction held open.

Also make the gateway's own failures legible. NIC rejections arrive as
{Status, ErrorDetails}, but failures the ASP raises in front of NIC use
{status_cd, error{error_cd, message}}, which shares no field name with RespPl: Gson
produced an all-null object and the stored reason degraded to the literal
'RespPl{Status=0, Data=null, ErrorDetails=null, InfoDtls=null}', indistinguishable from
NIC rejecting the document. RespGSPErr already modelled that envelope but was a
non-static inner class and so could not be instantiated by Gson; made it static and
copy the code and message into ErrorDetails, which every caller already reads.
 
37451 40 d 19 h amit /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/services/ Send authenticated mail as the account that actually authenticates

googleMailSender logs in to smtp.gmail.com as sdtech@smartdukaan.com, but callers
build their From as noreply@smartdukaan.com. Google Workspace binds an authenticated
session to one identity and refuses the mismatch with 535 'Authorization failed:
Authenticated user is not authorized to send mail' - the password is accepted, the
message is not. Every alert routed through that sender was therefore lost at the SMTP
layer, the e-invoice failure alert for NSUPHR1770 among them.

AuthenticatedIdentityMailSender rewrites From to the sender's own username at send
time, keeping whatever display name the caller chose. Senders that do not authenticate
are left alone: gmailRelaySender is the IP-authorised relay and has no username, and
sendGridMailSender's username is the literal 'apikey', so both keep noreply@.

Enforced in the sender rather than at the ~40 call sites that build their own
MimeMessage and hardcode noreply@, so it covers the ones added next too.
 

Show All