Subversion Repositories SmartDukaan

Rev

Show changed files | Directory listing | RSS feed

Filtering Options

Rev Age Author Path Log message Diff
37480 2 m amit /trunk/ mail: correct a wrong claim in r37479 -- the Google app password IS valid

r37479 stated googleMailSender's app password was rejected. That was wrong. The
test behind it resolved smtp.gmail.com over IPv6; repeating it over IPv4 with
the same credential gives AUTH OK on both 465 and 587.

The real fault is not the credential and not the bean config, both of which are
correct. SMTP from this host works over IPv4 only:

smtp.gmail.com IPv4 -> AUTH OK IPv6 -> 535 5.7.8 Username and Password not accepted
smtp-relay IPv4 -> 250 MAIL FROM IPv6 -> 550 5.7.1 Invalid credentials for relay

The JVM prefers IPv4, which is the only reason mail leaves this box at all.
Anything that prefers IPv6 fails on both paths.
 
37479 7 m amit /trunk/ mail: make the relay the default sender, not the Google identity

Correcting r37474-37476. Those made googleMailSender @Primary on the assumption
its credentials worked. Tested against the live servers from the prod host:

googleMailSender 535 5.7.8 Username and Password not accepted (BadCredentials)
-- the app password in the source is no longer valid
relay over IPv4 250 OK for MAIL FROM:<noreply@smartdukaan.com>
relay over IPv6 550 5.7.1 Invalid credentials for relay

So promoting google would have replaced one broken default with another. The
relay is what actually delivers today and it becomes 'mailSender'. It does not
authenticate -- Workspace authorises it by allowlisted source IP -- so sending
as noreply@ is legitimate there and AuthenticatedIdentityMailSender correctly
leaves it alone.

googleMailSender stays available by qualifier. Point the default back at it once
a valid app password is issued for sdtech@smartdukaan.com.

Also noted in the javadoc: the relay allowlist covers the IPv4 address only, so
anything that prefers IPv6 will be refused.
 
37478 18 m amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/ mail: MailOutboxService implements MailQueue

Adds the single entry point, queue(MailRequest). The twenty-odd queueMail*
overloads remain as delegates because ~112 call sites use them, but they now
funnel through one path rather than each building their own argument list.

Selecting the transport moves here too: MailSenderType.GOOGLE maps to the
authenticated sender, anything else to the relay.
 
37477 18 m amit /trunk/profitmandi-common/src/main/java/com/spice/profitmandi/common/ mail: one way to send mail, and nothing sends inline

There were three overlapping APIs -- Utils.sendMail* (7 static overloads),
EmailServiceImpl.sendMail* (7 more) and MailOutboxService.queueMail* (20). The
first two built their own MimeMessage, hardcoded From: noreply@smartdukaan.com
in eleven places, and pushed straight down whichever JavaMailSender the caller
passed.

That From is why mail was refused: an authenticated Workspace session may send
only as the account it logged in as, and noreply@ is a different identity in the
same domain. Sending inline also meant a failed send was lost outright and took
the calling job down with it, which is how one expired credential came to mark
ten report jobs FAILED.

New in profitmandi-common:
MailQueue - the single entry point; queue() returns once the mail is
recorded, not once it is sent
MailRequest - one value object replacing the 34 overloads
MailSenderType - GOOGLE (authenticated, From rewritten) or RELAY (IP-authorised)

The interface lives in common while MailOutboxService implements it in dao,
because dao depends on common and not the reverse -- Utils and EmailService
could not otherwise reach the outbox at all.

Utils and EmailService keep their signatures so the ~38 call sites still
compile, but now delegate and ignore the JavaMailSender argument; choosing a
transport was never the caller's business. Both are marked deprecated.
MailQueueHolder bridges the static helpers to the bean and is documented as a
compromise, not a pattern.
 
37476 38 m amit /trunk/profitmandi-fofo/src/main/java/com/spice/profitmandi/web/config/ mail: delete the dead SendGrid bean from fofo, make the authenticated identity primary

googleMailSender is now @Primary and answers to 'mailSender'. Two senders remain:
the authenticated Workspace identity and the IP-authorised relay.
 
37475 45 m amit /trunk/profitmandi-web/src/main/java/com/spice/profitmandi/web/config/ mail: delete the dead SendGrid bean from web, make the authenticated identity primary

Removes the live SendGrid bean plus two commented-out corpses (an old
build@shop2020.in sender and a dead alias). googleMailSender is now @Primary and
answers to 'mailSender', so unqualified injections get the working authenticated
sender instead of one that rejects with 535.
 
37474 45 m amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ mail: delete the dead SendGrid bean from cron, make the authenticated identity primary

The @Primary bean named 'mailSender' was SendGrid, so every unqualified
JavaMailSender injection got it -- and SendGrid rejects with 535, which is what
was failing 10 report/notification cron jobs (dailyTrackingReport,
monthlyTargetForPartner, sendFeebackSalesAndRbm and others). 420 occurrences in
one log.

googleMailSender now answers to both 'googleMailSender' and 'mailSender' and is
@Primary, so those injections resolve to a sender that works and whose From is
rewritten to the authenticated identity. No call site changes.
 
37473 46 m amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/service/mail/ mail: retire the SENDGRID sender label

SendGrid was never actually used by the outbox -- resolveSender() already fell
through to the Workspace relay for it, which is why outbox mail kept working
while direct JavaMailSender injections failed.

Two senders remain, each named for what it is:
GOOGLE - authenticates as sdtech@smartdukaan.com; AuthenticatedIdentityMailSender
rewrites every From to that identity, because Workspace binds an
authenticated session to one address and rejects any other with
'535 Authorization failed: Authenticated user is not authorized to send mail'
RELAY - Workspace relay, no auth, authorised by source IP, may send as noreply@

Default convenience methods now route to RELAY explicitly rather than through a
label that named a provider we do not use.
 
37472 1 h 49 m amit /trunk/profitmandi-cron/src/main/java/com/smartdukaan/cron/ IMEI activation: one snapshotted daily pass per brand, on one thread, with per-brand metrics

Four @Scheduled jobs every 5 minutes become two daily passes. Oppo and realme
share one thread and alternate in 25-imei chunks, so exactly one ChromeDriver is
alive at a time instead of four; vivo keeps its own thread since it is direct
HTTP and does not contend for a browser.

The pass snapshots its pool before any browser starts and walks that list to the
end. It never re-queries, and that is the actual fix. A failed lookup never
reaches dateMap.put, so no row is written, so createTimestamp is not bumped, so
the imei was eligible again on the next tick five minutes later. Measured 29-Aug:
realme issued 4,524 requests against 1,004 distinct imeis -- 4.5 asks each, 78%
of the day's budget spent re-asking -- while oppo, which rarely fails, sat at
1.03. More requests hardened the block, which caused more failures. A pass bounds
that: a failure costs one retry tomorrow, never one in five minutes.

This supersedes the r37447/r37448/r37449 argument about driver count, which was
about the wrong variable. That argument blamed realme's collapse on CPU
contention pushing the captcha render past the element waits. The logs do not
support it: on 29-Aug oppo took ZERO canvas timeouts across all 24 hours on the
same box, same six cores, same driver count, same captcha vendor, load average
0.9 -- including the 15:00-23:00 window in which realme solved nothing at all.
Realme's own canvas wait is 15s against oppo's 8s, so the longer wait is the one
expiring. What realme's timeout rate tracks is its own daily request volume, and
it resets at midnight: 920/day -> 0.3%, 3,467/day -> 28%, 4,524/day -> 75%. That
is realme.com declining to serve the widget.

DAYS=0 is deliberate and is not an off-by-one: the pool filter is
createTimestamp < now().atStartOfDay().minusDays(DAYS), so DAYS=1 measures
against yesterday midnight and silently yields a two-day cadence, which is what
oppo and realme were running.

Sizing measured on prod for a midnight start: oppo 4,133 and realme 2,118 imeis,
11.7h + 8.4h = 20.1 hours of a single thread. It fits with no slack; if the
'pass finished' counts come in short of 'pass starting', the lever is DAYS=1
rather than a second thread.

Observability: ImeiActivationGauges publishes the funnel per brand on
/actuator/prometheus, which alloy already scrapes on this host -- due, churned,
captcha_shown, captcha_solved, answered, dates_found, errors, run_seconds and
last_finish_epoch. Each stage fails differently and says what broke. Rates are
left to PromQL. The stage that matters for health is answered: churned>0 with
answered==0 is precisely the shape of both silent outages this year (oppo wrote
nothing for a week; the vivo captcha solver was dead for 46 days). dates_found is
deliberately NOT a health signal -- when the multi-year backlog drained at the
end of August, yield fell from ~100% to 2-3% on the same day across all three
brands with nothing broken.

Nagios cleanup: the Nagios server and every NRPE daemon are gone, so
WriteToPropertiesFile and the commented-out blocks that fed
nagios-cron.properties are deleted, and NagiosMonitorTasks is renamed
BalanceMonitorTasks for the transport it actually uses. Noted there that nothing
calls it -- there is no @Scheduled entry and no other caller -- which is why both
balance gauges have always read -1.
 
37471 1 h 49 m amit /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/ IMEI activation pools: skip stock billed today or yesterday

Both pool named queries now exclude serials whose sale is inside the last 48
hours -- o.billingTimestamp for the secondary path, fo.createTimestamp for the
tertiary one. A handset sold in the last two days has essentially never been
activated yet, so the lookup is spent for nothing: measured on prod, the
sold-within-180-days cohort returns an activation date on 2.4% (vivo) to 8.4%
(oppo) of lookups, against 43-97% for stock sold over a year ago.

billedBefore is set in ActivatedImeiRepositoryImpl rather than passed by the
caller, so no method signature changes and every brand inherits it -- vivo,
oppo, realme and motorola all draw from these two queries and nothing outside
profitmandi-cron uses them.

This is correctness rather than capacity: it trims 30 imeis from the oppo pool
and 9 from realme.
 
37470 2 h 7 m 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 4 h 16 m ranu /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ rbm drr dashboard , all today po rbm view maped  
37468 4 h 43 m ranu /trunk/profitmandi-fofo/src/main/webapp/WEB-INF/views/ftl/ rbm drr dashboard new ui looks  
37467 6 h 26 m 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 6 h 47 m 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 6 h 47 m 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 6 h 47 m 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 6 h 48 m 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 6 h 49 m 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 2 d 1 h ranu /trunk/profitmandi-dao/src/main/java/com/spice/profitmandi/dao/model/ rbm drr dashboard new ui looks  

Show All